引言
對程序語言設計者來說,設計一個令人滿意的I/O(輸入輸出)系統,是件極艱巨的任務,摘自《Thinking in Java 》
概述
JAVA程序通過流來完成輸入/輸出。流是生產或消費信息的抽象,流通過Java的輸入/輸出系統與物理設備連接。JAVA的I/O通過java.io包下的類和接口來支持,主要是5個類和一個接口。
- File:用于文件或目錄的描述信息,例如生成新目錄,修改文件名,刪除文件,判斷文件路徑等
- InputStream:抽象類,所有輸入流的父類,定義了所有輸入流具有的共同特征。
- OutputStream:抽象類,基于字節的輸出操作,所有輸出流的父類,定義了輸出流具有的共同特征。
- Reader:抽象類,基于字符的輸入類。
- Writer:抽象類,基于字符的輸出類。
- Serializable:將Java對象轉換成平臺無關的二進制數據
- RandomAccessFile:包裝了一個隨機訪問的文件,實現了DataInput/DataOutput接口。
File類
一個File類的對象,表示了磁盤上的文件或目錄,提供了與平臺無關的方法來對磁盤上的文件或者目錄進行操作,直接處理文件或目錄,但是不能訪問文件本身。
在java.io包中提供了60多了類流:
從功能上分為:
- 輸入流:從外部流向內存
- 輸出流:從內存流向外部
從流的角色上分為:
- 節點流:可以從/向一個特定的IO設備(磁盤,網絡)讀/寫數據的流,直接連接物理設備或socket
- 處理流:對一個已經存在的流(節點流)進行連接或者封裝。
從流的結構上分為:
- 字節流 :InputStream OutputStream
- 字符流 : Reader Writer
在最底層,所有的輸入輸出都是以字節為單位的,基于字符的流只是為處理字符提供方便有效的方法。它們的操作方式幾乎完全一樣,只是操作的數據單元不同,字節流操作的數據單元是字符,字符流操作的數據單元是字符。
類流全家福
從上面的圖片中,可以看出無論是字節流和字符流都提供了對文件,數組,字符串,管道,對象,音頻等內容的輸入/輸出流支持。
對輸入/輸出流基類的簡單介紹
InputSteam 三個基本的讀方法:
abstract int read(): 讀取一個字節數據,并返回讀到數據的末尾,如果返回-1 , 表示都到了輸入流的末尾。
int read(byte[] b ): 將數據讀入一個字節數組,同時返回實際讀取的字節數,如果返回-1 ,表示讀到了輸入流的末尾。
int read(byte[] b , int off , int len):將數據讀入一個字節數組,同時返回實際讀取的字節數,如果返回-1 ,表示讀到了輸入流的末尾,off指定在數組b中存放數據的起始位置,len指定讀取的最大字節數。
可能會有一個疑問,為什么上面的第一個方法是抽象,而其余兩個read方法是具體的? 通過源碼找到了想要的答案。第二個方法依靠了第三個方法,而第三個方法依靠了第一個方法實現,換句話說只有第一個read方法是與具體的I/O設備相關的,需要InputStream的子類來實現。源碼如下:
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {
return read(b, 0, b.length); //調用第三個方法實現
}
public int read(byte b[], int off, int len) throws IOException {
//判斷參數的合法性
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read(); //調用第一個方法,讀取一個字節
if (c == -1) {
return -1; //沒有字節返回-1 ,讀取結束
}
b[off] = (byte)c; //將讀取到的字節放入數組中
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c; //將讀取到的字節放入數組中,讀取的長度大于數據長度,循環讀取結束
}
} catch (IOException ee) {
}
return i; //返回讀取的字節數組長度
}
其它方法
long skip(long n ):輸入流中跳過n個字節,并返回實際跳過的字節數
void close():關閉流
。。。
OutputStream 主要方法
void write(byte[]] b ):將參數b中的字節寫到輸出流
void write(byte[] b , int off ,int len):將參數b的從偏移量off開始len個字節寫到輸出流
abstract viud write(int b ):先將int轉換成byte類型,把低字節寫入到輸出流中
void flush():將緩沖區中的字節全部輸出,并清空緩存區
void close():關閉輸出流并釋放與流相關的系統資源
Reader里包含的3個讀取方法:
int read():
int read(char[] c):
int read(char[] c ,int off , int len) :
Write包含的方法
void write(char[] c)
void write(String str)
void write(String str, int off, int len)
abstract flush()
void close()
根據上面圖片做一個簡單的總結:
- FileInputStream和FileOutputStream 節點流,用于對文件讀取或往文件中寫入字節流。如果在構造FileOutputStream時,文件已經存在,則覆蓋整個文件。
- BufferedInputStream和BufferedOutputStream過濾流,需要使用已經存在的節點流來構造,提供了讀寫的效率。
- DataInputStream和DataOutputStream 過濾流,需要使用已經存在的節點流來構造,提供了讀寫Java中基本數據類型的功能。
- PipedInputStream和PipedOutputStream 管道流,用于線程間的通信,一個線程的PipedInputStream對象從另一個線程的PipedOutputStream 對象讀取輸入,只要管道流有用,必須提供同時構造管道輸入流和管道輸出流。
- InputStreamReader和OutputStreamWriter類,用于處理字符流的基本類,用來在字節流和字符流之間搭一座橋。將字節流和字符流進行轉換。
- FileReader和FileWriter 可以創建一個可以讀/寫文件的類。
- BufferedReader:通過緩沖輸入提高性能。
- SequenceInputStream:把多個InputStream合并為一個InputStream
I/O流的鏈接示意圖:
常用類
FileInputStream , FileOutputStream , ByteArrayInputStream, ByteArrayOutputStream ,BufferedReader,BufferedWriter, ,InputStreamReader ,InputStreamWriter,FileReader ,FileWriter
如何選擇I/O流
確定是輸入還是輸出
輸入:輸入流 InputStream Reader
輸出:輸出流 OutputStream Writer
明確操作的數據對象是否是純文本
是:字符流 Reader,Writer
否:字節流 InputStream,OutputStream
明確具體的設備。
文件:
讀:FileInputStream,, FileReader,
寫:FileOutputStream,FileWriter
數組:
byte[ ]:ByteArrayInputStream, ByteArrayOutputStream
char[ ]:CharArrayReader, CharArrayWriter
String:
StringBufferInputStream(已過時,因為其只能用于String的每個字符都是8位的字符串), StringReader, StringWriter
Socket流
鍵盤:用System.in(是一個InputStream對象)讀取,用System.out(是一個OutoutStream對象)打印
是否需要轉換流
是,就使用轉換流,從Stream轉化為Reader、Writer:InputStreamReader,OutputStreamWriter
是否需要緩沖提高效率
是就加上Buffered:BufferedInputStream, BufferedOuputStream, BufferedReader, BufferedWrite
少年聽雨歌樓上,紅燭昏羅帳。
壯年聽雨客舟中,江闊云低,斷雁叫西風。
感謝支持!
---起個名忒難