1.流的分類
(1)輸入輸出流
輸入輸出是針對程序運行的內(nèi)存而言的
輸入流的基類:InputStream,Reader
輸出流的基類:OutputStream, Write
(2)字節(jié)流和字符流
用法幾乎一樣,只是字節(jié)流和字符流所操作的數(shù)據(jù)單元不同,字節(jié)流操作的數(shù)據(jù)單元是8位的 字節(jié),而字符流操作的數(shù)據(jù)單位是16為 字符
字節(jié)流和字符流的處理方式很像,最大的區(qū)別只是處理的單位數(shù)據(jù)量不同
注:一個字符(char)是2個字節(jié),一個字節(jié)是8位.
(3)節(jié)點流和處理流
節(jié)點流是程序直接到實際的數(shù)據(jù)源進行讀寫操作,節(jié)點流又被稱為低級流,如FileInputStream,ByteArrayInputStream
處理流是用一個已存在的流(即節(jié)點流?)進行連接或封裝,程序通過該封裝的流來個實際數(shù)據(jù)源進行讀寫操作,換言之,程序不和實際的數(shù)據(jù)源直接連接。這樣可以增強代碼的通用性,
注: 1.java中用處理流包裝節(jié)點流是一種典型的裝飾器設計模式,這樣通過使用處理流來包裝不同的節(jié)點流,既可以消除不同節(jié)點流的實現(xiàn)差異(即代碼編寫的差異),也可以提供更方便的方法來完成輸入輸出功能,因此處理流也被稱為包裝流
2.數(shù)據(jù)源可能是磁盤、硬盤、網(wǎng)絡等,而不同的數(shù)據(jù)源,其代碼的編寫是有差異的,如果將這些節(jié)點流包裝成處理流,就可以使用相同的輸入輸出代碼來讀寫不同設備的數(shù)據(jù)。
3.處理流的優(yōu)點: (1)性能提高: 主要以增加緩沖的方式來提高輸入/輸出的效率;(2)操作便捷: 可以大批量的處理數(shù)據(jù)。
處理可以 嫁接 到任何已存在的流的基礎之上,這樣就可以是的Java應用程序采用相同的代碼,透明的方式來訪問不同的輸入輸出設備的數(shù)據(jù)流。
2.字節(jié)流和字符流
字節(jié)流操作的數(shù)數(shù)據(jù)單元是 字節(jié)
字符流操作的數(shù)據(jù)單元是 字符(2個字節(jié))
(1)InputStream和Reader
InputStream和Reader是所有輸入流的抽象基類,是所有輸入流的模板。
InputStream里包含的方法:
int read(); //一次從輸入流中讀取單個字節(jié)的數(shù)據(jù),返回所讀取的字節(jié)數(shù)據(jù)(已轉(zhuǎn)換為int類型了)
int read(byte[] b); //一次最多讀取b.length個字節(jié)的數(shù)據(jù),并將其存儲在byte數(shù)組中,并返回實際讀取的字節(jié)個數(shù)。這個byte[]就相當于一個取水的竹筒,用他去取水,當然這個竹筒里會有水了啊,并且當水源不夠的時候這個竹筒并不能裝滿。方法返回這個竹筒里實際裝的水量
int read(byte[] b, int off, int len); //讀取len個字節(jié)數(shù)據(jù),并將這些數(shù)據(jù)存儲在byte數(shù)組b中,但是放入的時候是從數(shù)組的off位置開始放入的,方法返回實際讀取到的字節(jié)數(shù)
long skip(long n);//跳過并丟棄該輸入流中的n字節(jié)數(shù)據(jù)后,才準備開始寫入數(shù)據(jù)
void close(); //關(guān)閉IO資源
Reader里的方法
int read(); //一次從輸入流中讀取單個字符,返回所讀取的字符數(shù)據(jù)(已轉(zhuǎn)換為了int類型了)
int read(char[] cbuf); //一次最多讀取cbuf.length個字符的數(shù)據(jù),將讀取到的數(shù)據(jù)存儲到char數(shù)組中,并返回實際讀取到的字符個數(shù)
int read(char[] cbuf, int off, int len); //最多讀取len個字符數(shù)據(jù),并將讀取到的數(shù)據(jù)存儲到char數(shù)組中,從off位置開始存儲,返回實際讀取到的字符個數(shù)
void close(); //關(guān)閉IO資源
注:1.上面的read(Xxx[] b)方法返回-1時,表明數(shù)據(jù)已經(jīng)取完了
2.當沒有數(shù)據(jù)的時候,read()也會返回-1
3.直接將byte[]和char[]數(shù)組轉(zhuǎn)換為字符串
byte[] -> public String(byte[] bytes, int index, int length);
char[] -> public String(char[] chs, int index, int length);
4.注意數(shù)組長度的選取,如果太短,容易造成讀取中文字符(2個字節(jié))時出現(xiàn)亂碼
5.用完IO流后應該關(guān)閉其資源,因為這些IO資源不屬于內(nèi)存里的,故垃圾回收機制無法回收這些資源,因此需要手動關(guān)閉。Java7以后增加了自動關(guān)閉IO資源的機制,即只要處于try塊中的IO流在使用完畢后會自動關(guān)閉
(2)OutputStream和Write
OutputStream和Write是所有輸出流的抽象基類,是所有輸出流的模板。二者的用法完全一樣
OutputStream的方法
void write(int b); //將指定的字節(jié)(這里以int類型的形式表示該字節(jié))輸出到輸出流中。
void write(byte[] b); //將一組字節(jié)輸出到輸出流中
void write(byte[] b, int off, int len); //將一組字節(jié)從off位置開始,長度為len的字節(jié)輸出到輸出流中
void close()
void flush()
Write的方法
void write(int c); //將指定的字符(這里以int類型的形式表示該字符)輸出到輸出流中。
void write(char[] cbuf);
void write(char[] cbuf, int off, int len)
abstract void close(); //執(zhí)行這個方法前,系統(tǒng)會自動執(zhí)行flush()方法,
abstract void flush(); //刷新緩沖區(qū),將緩沖區(qū)中的數(shù)據(jù)刷新到物理節(jié)點中。
//因為字符流直接以字符作為操作單位,所以可以使用字符串來替代字符數(shù)組,因此,多了下面兩個方法
void write(String str)
void write(String str, int off, int len)
注意:
1.使用完輸出流后,切記一定要將IO流資源關(guān)閉,不僅可以保正資源被回收外,更重要的是可以將輸出緩沖區(qū)的數(shù)據(jù)flush到物理結(jié)點中。
2.輸出換行符時,不同的平臺不一樣,如Windows是\r\n, 而在UNIX/LINUX中\(zhòng)n
3.輸入輸出體系
節(jié)點流(如FileInputStream, FileOutputStream)使用起來很繁瑣,因此為了簡化程序的編寫,包裝出了處理流。
使用處理流的典型思路:使用處理流來包裝節(jié)點流,讓程序通過處理流來執(zhí)行輸入輸出的功能,而節(jié)點流只需負責與底層的I/O設備、文件交互。
從名稱上來識別處理流和節(jié)點流:只要流的構(gòu)造器參數(shù)不是一個物理節(jié)點(如stringPath, File等),而是一個已經(jīng)存在的流(如OutputStream),那么該流就一定是處理流,而所有的節(jié)點流的構(gòu)造器都是以物理IO節(jié)點作為參數(shù)。
使用處理流很簡單,只需給該處理流傳入一個節(jié)點流作為構(gòu)造器參數(shù)即可,這樣創(chuàng)建的處理流就是包裝了該節(jié)點流的處理流。
注意:
1.使用處理流時,只需關(guān)閉最上層的處理流即可,底層的節(jié)點流會自動關(guān)閉
2.如果輸出輸入的內(nèi)容是文本時,應該采用字符流(更高效),如果進行輸入輸出是二進制內(nèi)容時,則應該采使用字節(jié)流
3.Windows下簡體中文默認的使用GBK字符集,Linux下默認是UTF-8字符集
4.幾個重要的輸入輸出流
(1)FileInputStream和FileOutputStream、FileReader和FileWrite
前面講過
(2)BufferedInputStream和BufferedOutputStream、BufferedReader和BufferedWriter
BufferedInputStream
BufferedInputStream(InputStream in);
BufferedInputStream(InputStream in, int size)
int read()
int read(byte[] b, int off, int len)
BufferedOutputStream
BufferedOutputStream(OutputStream out);
BufferedOutputStream(OutputStream out, int size)
void write(byte[] b, int off, int len)
void write(int b)
BufferedReader
BufferedReader(Reader in);
BufferedReader(Reader in, int sz)
int read();
int read(char[] cbuf, int off, int len)
String readLine(); //一次讀取一行,沒有數(shù)據(jù)可讀時會返回null
例子
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(a.java)));
String line;
while(line = br.readLine() != null) {
System.out.println(line);
}
BufferedWriter
BufferedWriter(Writer out);
BufferedWriter(Writer out, int sz)
void write(char[] cbuf, int off, int len)
void write(int c)
void write(String s, int off, int len)
void newLine(); //根據(jù)系統(tǒng)來決定換行符
void close()
void flush()
注意:這種處理流體現(xiàn)的是一種裝飾器設計模式。
(3)InputStreamReader和OutputStreamWrite
將字節(jié)流轉(zhuǎn)換為字符流
InputStreamReader
InputStreamReader(InputStream in); //
InputStreamReader(InputStream in, Charset cs); //指定字符集
InputStreamReader(InputStream in, CharsetDecoder dec)
InputStreamReader(InputStream in, String charsetName)
int read()
int read(char[] cbuf, int offset, int length)
OutputStreamWrite
OutputStreamWriter(OutputStream out);
OutputStreamWriter(OutputStream out, Charset cs);
OutputStreamWriter(OutputStream out, CharsetEncoder enc);
OutputStreamWriter(OutputStream out, String charsetName)
void write(char[] cbuf, int off, int len);
void write(int c);
void write(String str, int off, int len);
void close();
void flush();
注意:這兩個轉(zhuǎn)換流也是一種處理流,體現(xiàn)的是一種適配器模式
(4)PrintStream和PrintWrite
打印流,即輸出流
PrintStream
PrintStream(File file)
PrintStream(OutputStream out)
PrintStream(OutputStream out, boolean autoFlush);
PrintWrite
PrintWriter(File file);
PrintWriter(OutputStream out);
PrintWriter(OutputStream out, boolean autoFlush);
PrintStream的輸出功能非常強大,通常如果需要輸出文本內(nèi)容,都應該考慮將輸出流包裝成PrintStream后進行輸出
注:1.常常在處理文本文件時,將InputStream轉(zhuǎn)換為Reader,再進一步包裝成BufferedReader。
2.將OutputStream轉(zhuǎn)換為Write,再進一步包裝成BufferedWrite。
3.Scanner sc = new Scanner(System.in); System.in的本質(zhì)是InputStream 轉(zhuǎn)換為 ->
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine();
4.RandomAccessFile(隨機訪問文件流)
可以自由訪問文件的任意地方,所以,如果是只需訪問文件部分內(nèi)容,而不是從頭讀到尾,則使用這個是更好的選擇。
注意:RandomAccessFile只能讀寫文件,不能讀取其他的IO節(jié)點
RandomAccessFile(File file, String mode); //mode是讀寫模式,'r','rw','rws','rwd'
RandomAccessFile(String name, String mode)
long getFilePointer(); //返回文件記錄指針的當前位置
void setLength(long newLength); //設定文件大小
void seek(long pos); //將文件的記錄指針定位到pos位置(是相對于開頭處的跳躍),是絕對位移
int skipBytes(int n);//跳過n個字節(jié)(是相對于當前位置的跳躍),是相對位移
讀寫方法同InputStream,OutputStream
5.對象序列化(Serialize)與反序列化(Deserialize)
對象序列話的目的是為了將對象保存在磁盤(持久存儲)或者在網(wǎng)絡中直接傳輸對象,
對象序列化是指將一個Java對象寫入IO流中,與此對應的是反序列化從IO流中恢復該Java對象
實現(xiàn)一個對象序列化,則該對象必須實現(xiàn)Serializable(或Externalizable接口)接口,這是個標記接口
利用ObjectOutputStream和ObjectInputStream來實現(xiàn)對象的序列化輸出和反序列化輸入
ObjectOutputStream(OutputStream out);
void writeObject(Object obj); //將對象序列化后寫入輸出流中
ObjectInputStream(InputStream in);
Object readObject(); //從輸入流中讀取對象, 注意類型轉(zhuǎn)換