第10章 流
2019獨角獸企業(yè)重金招聘Python工程師標準>>>
File 類
- java.io.File類:文件和目錄路徑名的抽象表示形式,與平臺無關。
- File 能新建、刪除、重命名文件和目錄,但 File 不能訪問文件內(nèi)容本身。 如果需要訪問文件內(nèi)容本身,則需要使用 輸入/輸出流。
- File對象可以作為參數(shù)傳遞給流的構(gòu)造函數(shù)。
File的靜態(tài)屬性
String separator存儲了當前系統(tǒng)的路徑分隔符。 在UNIX中,此字段為‘/’,在Windows中,為‘\’。
File類的常見構(gòu)造方法
public File(String pathname)
以pathname為路徑創(chuàng)建File對象,可以是絕對路徑或者相對路徑,如果pathname是相對路徑,則默認的當前路徑在系統(tǒng)屬性user.dir中存儲。
public File(String parent,String child)
以parent為父路徑,child為子路徑創(chuàng)建File對象。
File類的常見方法
- 訪問文件名:
getName() getPath() getAbsoluteFile() getAbsolutePath() getParent() renameTo(File newName)
- 文件檢測:
exists() canWrite() canRead() isFile() isDirectory()
- 獲取常規(guī)文件信息:
lastModified() length()
- 文件操作相關:
createNewFile() delete()
- 目錄操作相關:
mkDir() mkDirs() list() listFiles()
File dir1 = new File("D:/IOTest/dir1"); // 如果D:/IOTest/dir1不存在,就創(chuàng)建為目錄 if (!dir1.exists()) {dir1.mkdir(); } // 創(chuàng)建以dir1為父目錄,名為"dir2"的File對象 File dir2 = new File(dir1, "dir2"); if (!dir2.exists()) { // 如果還不存在,就創(chuàng)建為目錄dir2.mkdirs(); } File dir4 = new File(dir1, "dir3/dir4"); if (!dir4.exists()) {dir4.mkdirs(); } // 創(chuàng)建以dir2為父目錄,名為"test.txt"的File對象 File file = new File(dir2, "test.txt"); if (!file.exists()) { // 如果還不存在,就創(chuàng)建為文件try {file.createNewFile();} catch (IOException e) {e.printStackTrace();} }練習
利用File構(gòu)造器,new 一個目錄file
1)在其中創(chuàng)建多個文件和目錄。
2)編寫方法,實現(xiàn)刪除file中文件的操作。
Java IO原理
- IO流用來處理設備之間的數(shù)據(jù)傳輸。
- Java程序中,對于數(shù)據(jù)的輸入/輸出操作以“流(stream)”的方式進行。
- java.io包下提供了各種“流”類和接口,用以獲取不同種類的數(shù)據(jù),并通過標準的方法輸入或輸出數(shù)據(jù)。
- 輸入input:讀取外部數(shù)據(jù)(磁盤、光盤等存儲設備的數(shù)據(jù))到程序(內(nèi)存)中。
- 輸出output:將程序(內(nèi)存)數(shù)據(jù)輸出到磁盤、光盤等存儲設備中。
流的分類
- 按操作數(shù)據(jù)單位不同分為:字節(jié)流(8 bit),字符流(16 bit) 。
- 按數(shù)據(jù)流的流向不同分為:輸入流,輸出流。
- 按流的角色的不同分為:節(jié)點流,處理流。
- Java的IO流共涉及40多個類,實際上非常規(guī)則,都是從如下4個抽象基類派生的。
- 由這四個類派生出來的子類名稱都是以其父類名作為子類名后綴。
IO 流體系
節(jié)點流和處理流
- 節(jié)點流可以從一個特定的數(shù)據(jù)源讀寫數(shù)據(jù)。
- 處理流是“連接”在已存在的流(節(jié)點流或處理流)之上,通過對數(shù)據(jù)的處理為程序提供更為強大的讀寫功能。
InputStream & Reader
- InputStream 和 Reader 是所有輸入流的基類。
- InputStream(典型實現(xiàn):FileInputStream)。
int read(); int read(byte[] b); int read(byte[] b, int off, int len);
- Reader(典型實現(xiàn):FileReader)
int read(); int read(char [] c); int read(char [] c, int off, int len);
- 程序中打開的文件IO資源不屬于內(nèi)存里的資源,垃圾回收機制無法回收該資源,所以應該顯式關閉文件IO資源。
OutputStream & Writer
- OutputStream 和 Writer 是所有輸出流的基類。
- OutputStream(典型實現(xiàn):FileOutputStream)。
void write(int b/int c); void write(byte[] b/char[] cbuf); void write(byte[] b/char[] buff, int off, int len); void flush(); void close(); //需要先刷新,再關閉此流。
- 因為字符流直接以字符作為操作單位,所以 Writer 可以用字符串來替換字符數(shù)組,即以 String 對象作為參數(shù)。
void write(String str); void write(String str, int off, int len);
文件流
讀取文件
- 建立一個流對象,將已存在的一個文件加載進流。 FileReader fr = new FileReader(“Test.txt”);
- 創(chuàng)建一個臨時存放數(shù)據(jù)的數(shù)組。 char[] ch = new char[1024];
- 調(diào)用流對象的讀取方法將流中的數(shù)據(jù)讀入到數(shù)組中。 fr.read(ch);
寫入文件
-
創(chuàng)建流對象,建立數(shù)據(jù)存放文件。
FileWriter fw = new FileWriter("Test.txt") -
調(diào)用流對象的寫入方法,將數(shù)據(jù)寫入流。
fw.write("text") -
關閉流資源,并將流中的數(shù)據(jù)清空到文件中。
fw.close()
例:
FileWriter fw = null; try {fw = new FileWriter("Test.txt");fw.write("text"); } catch (IOException e) {e.printStackTrace(); } finally {if (fw != null) {try {fw.close();} catch (IOException e) {System.out.println(e.toString());}} }注意
- 定義文件路徑時,注意:可以用“/”或者“\”。
- 在寫入一個文件時,如果目錄下有同名文件將被覆蓋。
- 在讀取文件時,必須保證該文件已存在,否則出異常。
處理流之一:緩沖流
- 為了提高數(shù)據(jù)讀寫的速度,Java API提供了帶緩沖功能的流類,在使用這些流類時,會創(chuàng)建一個內(nèi)部緩沖區(qū)數(shù)組。
- 根據(jù)數(shù)據(jù)操作單位可以把緩沖流分為:
BufferedInputStream 和 BufferedOutputStream
BufferedReader 和 BufferedWriter
- 緩沖流要“套接”在相應的節(jié)點流之上,對讀寫的數(shù)據(jù)提供了緩沖的功能,提高了讀寫的效率,同時增加了一些新的方法。
- 對于輸出的緩沖流,寫出的數(shù)據(jù)會先在內(nèi)存中緩存,使用flush()將會使內(nèi)存中的數(shù)據(jù)立刻寫出。
練習
分別使用
節(jié)點流:FileInputStream、FileOutputStream
和
緩沖流:BufferedInputStream、BufferedOutputStream
實現(xiàn)文本文件/圖片/視頻文件的復制。
并比較二者在數(shù)據(jù)復制方面的效率。
處理流之二:轉(zhuǎn)換流
- 轉(zhuǎn)換流提供了在字節(jié)流和字符流之間的轉(zhuǎn)換。
- Java API提供了兩個轉(zhuǎn)換流:
InputStreamReader 和 OutputStreamWriter
- 字節(jié)流中的數(shù)據(jù)都是字符時,轉(zhuǎn)成字符流操作更高效。
InputStreamReader
- 用于將字節(jié)流中讀取到的字節(jié)按指定字符集解碼成字符。需要和InputStream“套接”。
- 構(gòu)造方法:
public InputStreamReader(InputStream in)
public InputSreamReader(InputStream in,String charsetName)
如:
Reader isr = new InputStreamReader(System.in,”ISO5334_1”);
OutputStreamWriter
- 用于將要寫入到字節(jié)流中的字符按指定字符集編碼成字節(jié)。需要和OutputStream“套接”。
- 構(gòu)造方法:
public OutputStreamWriter(OutputStream os)
public OutputSreamWriter(OutputStream os,String charsetName)
補充:字符編碼
- 編碼表的由來
計算機只能識別二進制數(shù)據(jù),早期由來是電信號。為了方便應用計算機,讓它可以識別各個國家的文字。就將各個國家的文字用數(shù)字來表示,并一一對應,形成一張表。這就是編碼表。
- 常見的編碼表
ASCII:美國標準信息交換碼。用一個字節(jié)的7位可以表示。 ISO8859-1:拉丁碼表。歐洲碼表。用一個字節(jié)的8位表示。 GB2312:中國的中文編碼表。 GBK:中國的中文編碼表升級,融合了更多的中文文字符號。 Unicode:國際標準碼,融合了多種文字。所有文字都用兩個字節(jié)來表示,Java語言使用的就是unicode。 UTF-8:最多用三個字節(jié)來表示一個字符。
- 編碼:字符串 → 字節(jié)數(shù)組
- 解碼:字節(jié)數(shù)組 → 字符串
- 轉(zhuǎn)換流的編碼應用
可以將字符按指定編碼格式存儲。 可以對文本數(shù)據(jù)按指定編碼格式來解讀。 指定編碼表的動作由構(gòu)造器完成。
練習
將“你好”兩個字符查指定的utf-8的碼表,獲取對應的數(shù)字,并寫入到text.txt文件中。
public class Test {public static void main(String[] args) throws IOException {OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("text.txt"), "utf-8");osw.write("你好");osw.close();//讀取硬盤上的文件數(shù)據(jù),將獲取到的數(shù)據(jù)查指定utf-8 的碼表來解析該數(shù)據(jù)。InputStreamReader isr = new InputStreamReader(new FileInputStream("text.txt"), "utf-8");char[] buf = new char[10];int num = isr.read(buf);String s = new String(buf, 0, num);System.out.println(s);//傳入編碼表的方法都會拋出不支持編碼異常(UnsupportedEncodingException);} }處理流之三:標準輸入輸出流
- System.in和System.out分別代表了系統(tǒng)標準的輸入和輸出設備。
- 默認輸入設備是鍵盤,輸出設備是顯示器。
- System.in的類型是InputStream。
- System.out的類型是PrintStream,其是OutputStream的子類FilterOutputStream 的子類。
- 通過System類的setIn,setOut方法對默認設備進行改變。 public static void setIn(InputStream in) public static void setOut(PrintStream out)
例題
從鍵盤輸入字符串,要求將讀取到的整行字符串轉(zhuǎn)成大寫輸出。然后繼續(xù)進行輸入操作,直至當輸入“e”或者“exit”時,退出程序。
public class Test {public static void main(String[] args) {System.out.println("請輸入信息(退出輸入e或exit):");//把"標準"輸入流(鍵盤輸入)這個字節(jié)流包裝成字符流,再包裝成緩沖流BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String s = null;try {while ((s = br.readLine()) != null) { //讀取用戶輸入的一行數(shù)據(jù) --> 阻塞程序if (s.equalsIgnoreCase("e") || s.equalsIgnoreCase("exit")) {System.out.println("安全退出!!");break;}//將讀取到的整行字符串轉(zhuǎn)成大寫輸出System.out.println("-->:" + s.toUpperCase());System.out.println("繼續(xù)輸入信息");}} catch (IOException e) {e.printStackTrace();} finally {try {if (br != null) {br.close(); //關閉過濾流時,會自動關閉它包裝的底層節(jié)點流}} catch (IOException e) {e.printStackTrace();}}} }或者
public class Test5 {public static void main(String[] args) {Scanner input = new Scanner(System.in);System.out.println("請輸入字符串:");while (input.hasNext()) {String line = input.nextLine();if ("e".equals(line) || "exit".equals(line)) {System.exit(0);}String upperStr = line.toUpperCase();System.out.println(upperStr);}} }處理流之四:打印流(了解)
- 在整個IO包中,打印流是輸出信息最方便的類。
- PrintStream(字節(jié)打印流)和PrintWriter(字符打印流)。
提供了一系列重載的print和println方法,用于多種數(shù)據(jù)類型的輸出。 PrintStream和PrintWriter的輸出不會拋出異常。 PrintStream和PrintWriter有自動flush功能。 System.out返回的是PrintStream的實例。
public class Test {public static void main(String[] args) {FileOutputStream fos = null;try {fos = new FileOutputStream(new File("D:\\IO\\text.txt"));} catch (FileNotFoundException e) {e.printStackTrace();}//創(chuàng)建打印輸出流,//設置為自動刷新模式(寫入換行符或字節(jié) '\n' 時都會刷新輸出緩沖區(qū))PrintStream ps = new PrintStream(fos, true);if (ps != null) { // 把標準輸出流(控制臺輸出)改成文件System.setOut(ps);}for (int i = 0; i <= 255; i++) { //輸出ASCII字符System.out.print((char) i);if (i % 50 == 0) { //每50個數(shù)據(jù)一行System.out.println(); // 換行}}ps.close();} }處理流之五:數(shù)據(jù)流(了解)
- 為了方便地操作Java語言的基本數(shù)據(jù)類型的數(shù)據(jù),可以使用數(shù)據(jù)流。
- 數(shù)據(jù)流有兩個類:(用于讀取和寫出基本數(shù)據(jù)類型的數(shù)據(jù))
DataInputStream 和 DataOutputStream
分別“套接”在 InputStream 和 OutputStream 節(jié)點流上。
- DataInputStream中的方法:
boolean readBoolean()
char readChar()
float readFloat()
double readDouble()
byte readByte()
short readShort()
int readInt()
long readLong()
String readUTF()
void readFully(byte[] b)
- DataOutputStream中的方法
將上述的方法的read改為相應的write即可。
public class Test {public static void main(String[] args) {DataOutputStream dos = null;try {//創(chuàng)建連接到指定文件的數(shù)據(jù)輸出流對象dos = new DataOutputStream(new FileOutputStream("d:\\IOTest\\destData.dat"));dos.writeUTF("ab中國"); //寫UTF字符串dos.writeBoolean(false); //寫入布爾值dos.writeLong(1234567890L); //寫入長整數(shù)System.out.println("寫文件成功!");} catch (IOException e) {e.printStackTrace();} finally { //關閉流對象try {if (dos != null) {// 關閉過濾流時,會自動關閉它包裝的底層節(jié)點流dos.close();}} catch (IOException e) {e.printStackTrace();}}} }處理流之六:對象流
- ObjectInputStream 和 OjbectOutputSteam
用于存儲和讀取對象的處理流。它的強大之處就是可以把Java中的對象寫入到數(shù)據(jù)源中,也能把對象從數(shù)據(jù)源中還原回來。
- 序列化(Serialize):用ObjectOutputStream類將一個Java對象寫入IO流中。
- 反序列化(Deserialize):用ObjectInputStream類從IO流中恢復該Java對象。
ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變量。
對象的序列化
- 對象序列化機制允許把內(nèi)存中的Java對象轉(zhuǎn)換成平臺無關的二進制流,從而允許把這種二進制流持久地保存在磁盤上,或通過網(wǎng)絡將這種二進制流傳輸?shù)搅硪粋€網(wǎng)絡節(jié)點。當其它程序獲取了這種二進制流,就可以恢復成原來的Java對象
- 序列化的好處在于可將任何實現(xiàn)了Serializable接口的對象轉(zhuǎn)化為字節(jié)數(shù)據(jù),使其在保存和傳輸時可被還原
- 序列化是 RMI(Remote Method Invoke – 遠程方法調(diào)用)過程的參數(shù)和返回值都必須實現(xiàn)的機制,而 RMI 是 JavaEE 的基礎。因此序列化機制是 JavaEE 平臺的基礎
- 如果需要讓某個對象支持序列化機制,則必須讓其類是可序列化的,為了讓某個類是可序列化的,該類必須實現(xiàn)如下兩個接口之一:
Serializable
Externalizable
- 凡是實現(xiàn)Serializable接口的類都有一個表示序列化版本標識符的靜態(tài)變量: private static final long serialVersionUID;
serialVersionUID用來表明類的不同版本間的兼容性。 如果類沒有顯示定義這個靜態(tài)變量,它的值是Java運行時環(huán)境根據(jù)類的內(nèi)部細節(jié)自動生成的。若類的源代碼作了修改,serialVersionUID 可能發(fā)生變化。故建議,顯示聲明。
- 顯示定義serialVersionUID的用途:
希望類的不同版本對序列化兼容,因此需確保類的不同版本具有相同的serialVersionUID。 不希望類的不同版本對序列化兼容,因此需確保類的不同版本具有不同的serialVersionUID。
使用對象流序列化對象
- 若某個類實現(xiàn)了 Serializable 接口,該類的對象就是可序列化的:
創(chuàng)建一個 ObjectOutputStream。
調(diào)用 ObjectOutputStream對象的writeObject()方法輸出可序列化對象。
注意寫出一次,操作flush()。
- 反序列化
創(chuàng)建一個 ObjectInputStream。
調(diào)用 readObject() 方法讀取流中的對象。
- 強調(diào):如果某個類的字段不是基本數(shù)據(jù)類型或 String 類型,而是另一個引用類型,那么這個引用類型必須是可序列化的,否則擁有該類型的 Field 的類也不能序列化。
RandomAccessFile 類
- RandomAccessFile 類支持 “隨機訪問” 的方式,程序可以直接跳到文件的任意地方來讀、寫文件。
支持只訪問文件的部分內(nèi)容。
可以向已存在的文件后追加內(nèi)容。
- RandomAccessFile 對象包含一個記錄指針,用以標示當前讀寫處的位置。RandomAccessFile 類對象可以自由移動記錄指針:
long getFilePointer():獲取文件記錄指針的當前位置。 void seek(long pos):將文件記錄指針定位到 pos 位置。
- 構(gòu)造器
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
- 創(chuàng)建 RandomAccessFile 類實例需要指定一個 mode 參數(shù),該參數(shù)指定 RandomAccessFile 的訪問模式:
r: 以只讀方式打開。
rw:打開以便讀取和寫入。
rwd:打開以便讀取和寫入;同步文件內(nèi)容的更新。
rws:打開以便讀取和寫入;同步文件內(nèi)容和元數(shù)據(jù)的更新。
讀取文件內(nèi)容
public class Test {public static void main(String[] args) throws Exception {RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");raf.seek(5);byte[] b = new byte[1024];int off = 0;int len = 5;raf.read(b, off, len);String str = new String(b, 0, len);System.out.println(str);raf.close();} }寫入文件內(nèi)容
public class Test {public static void main(String[] args) throws Exception {RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");raf.seek(5);//先讀出來String temp = raf.readLine();raf.seek(5);raf.write("xyz".getBytes());raf.write(temp.getBytes());raf.close();} }流的基本應用小節(jié)
- 流是用來處理數(shù)據(jù)的。
- 處理數(shù)據(jù)時,一定要先明確數(shù)據(jù)源,與數(shù)據(jù)目的地。
數(shù)據(jù)源可以是文件,可以是鍵盤。
數(shù)據(jù)目的地可以是文件、顯示器或者其他設備。
-
而流只是在幫助數(shù)據(jù)進行傳輸,并對傳輸?shù)臄?shù)據(jù)進行處理,比如過濾處理、轉(zhuǎn)換處理等。
-
字節(jié)流-緩沖流(重點)
輸入流:InputStream、FileInputStream、BufferedInputStream
輸出流:OutputStream、FileOutputStream、BufferedOutputStream
- 字符流-緩沖流(重點)
輸入流:Reader、FileReader、BufferedReader
輸出流:Writer、FileWriter、BufferedWriter
- 轉(zhuǎn)換流(字節(jié)流轉(zhuǎn)換為字符流)
InputSteamReader、OutputStreamWriter
- 對象流(難點)
序列化:ObjectInputStream
反序列化:ObjectOutputStream
- 隨機存取流(掌握讀取、寫入)
RandomAccessFile
轉(zhuǎn)載于:https://my.oschina.net/mondayer/blog/3027711
總結(jié)
- 上一篇: yum仓库、源以及编译安装笔记
- 下一篇: Go语言很好很强大,但我有几个问题想吐槽