java.io
通過數據流、序列化和文件系統提供系統輸入和輸出。
我有一個習慣,在學習新的知識體系時,總是先將它的類層次結構搞清楚,這樣可以對類有一個全局觀。下面先看一下 java IO 系統的類關係圖
字符流:
字節流:
一、File 類
File 類是文件和目錄路徑的抽象表示形式。File 類定義了一些與平臺無關的方法來操縱文件,例如:創建、刪除和重命名文件。
編程舉例:判斷某個文件是否存在,存在則刪除,不存在則創建。
二、流
流是字節序列的抽象概念。文件是數據的靜態存儲形式,而流是指數據傳出時的形態。
三、字節流
1、InputStream 類
程序可以衝中連續讀取字節的對象叫輸入流,在 Java 中,用 InputStream 類來描述所有輸入流的抽象概念。
InputStream 類的方法:
- int read() :從輸入流中讀取一個字節的內容,並且把這個內容以整數的形式反回,如果遇到流的結束處,則返回 -1 。如果流沒有結束,但是暫時沒有數據可讀,read() 方法將阻塞,直到有新的數據可讀。因爲 read() 只讀取了一個字節的數據,read() 方法會將這一個字節的數據寫在 int 4字節中的最低位,其它高字節位全部設爲 0 ,這個過程與 byte 轉爲 int 的過程是不一樣的。因此 read() 方法返回的整數應在 0~255 之間,這樣就可以與表示流結束的 -1 數值進行區別。
- int read(byte[] b) :從輸入流中讀取若干個字節的內容到 byte 數組中,最多讀取的數據長度爲 byte 數組的長度。返回值爲實際讀取到的字節數。
- int read(byte[] b, int off, int len) :從輸入流中讀取 len 個字節的數據,然後將讀取的數據從數組 b 的角標開始存放到數組中,和上面一樣,返回值爲實際讀取到的字節數。
- long skip(long n) :跳過輸入流中的 n 個字節,並返回實際跳過的字節數。主要用於包裝流中。除了包裝流類,其它流只能順序讀取數據。
- int avaliable() :返回流中可讀的字節數。檢查流中是否有數據可讀,避免程序發生阻塞。
- void mark(int readlimit) :用與在輸入流中,建立一個標記,從 readlimit 位置開始,最多還能讀取多少數據。用於包裝類。
- void reset() :於 mark() 方法結合使用,讓指針跳到當前標記出。
- boolean markSupported() :返回當前流對象,是否支持 mark() 和 reset() 操作。
- void close() :將流對象關閉,以釋放與流對象相關的資源。垃圾回收期只會清除沒有變量引用的對象資源,而不能清除系統產生的系統資源,因此在流結束後要使用 close() 方法。
InputStream 是抽象類,實際使用到的是 InputStream 的子類,但並不是所有的子類對象都具有上面的方法 。
(1)FileInputStream 用來創建磁盤文件的輸入流對象,通過 FileInputStream 的構造函數來指定文件路徑和文件名。創建 FileInputStream 實例對象時,指定的文件應該是存在的和可讀的。對於一個磁盤文件創建 FileInputStream 對象的兩種方式:
1、FileInputStream in = new FileInputStream("fileName");
2、File f = new File("fileName");
FileInputStream in = new FileInputStream(f);
編程舉例:用 FileOutputStream 類向文件中寫入一個字符串,然後用 FileInputStream 讀出寫入的內容。
(2)PipedInputStream 和 PipedInputStream 用於在應用程序中創建管道通信。一個 PipedInputStream 對象必須與一個 PipedOutputStream 對象進行連接產生一個通信管道。PipedOutoutStream 對象負責向管道中寫入數據, PipedInputStream 對象負責從管道中讀取數據。主要完成線程之間的通信。一個線程的 PipedInputStream 能從另一個線程的 PipedOutputStream 對象中讀取數據。
編程舉例:實現兩個線程之間的通信
使用管道流類,可以實現各個程序模塊之間的松耦合通道。
(3)ByteArrayInputStream 用於以 IO 流的方式來完成對字節數組內容讀寫,來支持類似內存虛擬文件或者內存映像文件的功能。
構造函數:
1、ByteArrayInputStream(byte[] b)
2、ByteArrayInputStream(byte[] b, int offset, int length)
編程舉例:編寫一個函數,把輸入六中所有英文字母編程大寫字母后,將結果寫入帶一個輸出流對象,用這個函數來將一個字符串中的所有字符轉換成大寫。
(4)System.in 和 System.out System.in 連接到鍵盤,是 InputStream 類型的實例對象。System.out 連接到顯示器,是 PrintStream 類的實例對象。
編程舉例:藉助上一個杉樹,將鍵盤上輸入的內容轉爲大寫字母后打印在屏幕上。
(5)DataInputStream 並沒有對應到任何具體的流設備,一定要給它傳遞一個對應具體流設備的輸入流對象。DataInputStream 爲包裝類。構造 DataInputStream 的方法:
DataInputStream(InputStream in)
DateInputStream 提供了一個讀取字符串的方法:
1、public final String readUTF();
DateOutputStream 提供了三個寫入字符串的方法:
1、public final void writeBytes(String s);
2、public final void writeChars(String s);
3、public fianl void writeUTF(String s);
方法:
boolean
|
readBoolean
()
參見 DataInput
的 readBoolean
方法的常規協定。 |
byte
|
readByte
()
參見 DataInput
的 readByte
方法的常規協定。 |
char
|
readChar
()
參見 DataInput
的 readChar
方法的常規協定。 |
double
|
readDouble
()
參見 DataInput
的 readDouble
方法的常規協定。 |
float
|
readFloat
()
參見 DataInput
的 readFloat
方法的常規協定。 |
int
|
readInt
()
參見 DataInput
的 readInt
方法的常規協定。 |
long
|
readLong
()
參見 DataInput
的 readLong
方法的常規協定。 |
short
|
readShort
()
參見 DataInput
的 readShort
方法的常規協定。 |
(6)BufferedInputStream Java 提供的兩個緩衝區包裝類,不管底層系統是否使用了緩衝區,這兩個類在自己的實例對象中創建緩衝區。依然是用 InputStream 類的實例來構造自己。
流棧的包裝過程:
編程實例:分別使用 DataOutputStream 類的 writeUTF、writeBytes、writeChars 方法,比較這幾個方法的差異。
(7)ObjectInputStream ObjectInputSteam 包裝類用於從底層輸入流中讀取對象類型的數據和將對象類型的數據寫入到底層輸出流。只要把一個對象中的所有成員變量存儲起來就等於保存了這個對象。ObjectInputStream 所要寫的對象必須實現了 Serializable 接口,Serializable 接口中沒有方法,是一個空的接口,它的作用就是爲編譯器做標記。對象中的 transient(臨時變量標記,例如線程對象可以被 transient 修飾,因爲沒有保存的必要)和 static 類型的成員變量不會被寫入。
編程舉例:創建一個可序列化的學生對i向,並用 ObjectOutputStream 類把它存儲到一個文件中,然後再用 ObjectInputStream 類把存儲的數據讀取到一個學生對象中,即反序列化。
2、OutputStream 類
程序可以向其中連續寫入字節的對象叫輸出流,在 Java 中,用 OutputStream 類來描述所有輸出流的抽象概念。
OutputStream 類的方法:
- void write(int b) :將一個整數中的最低的一個字節的內容寫到輸出流中,其它高字節省去。
- void write(byte[] b) :將一個字節數組 b 中的所有內容寫入到輸出流中。
- void write(byte[] b, int off, int len) :將字節數組中從 off 個字節開始,一直寫到 第 len 個字節。
- void flush() :將內存緩衝區中的內容徹底的清空,並且輸出到 I/O 設備中。
- void close() :關閉流對象,並釋放資源。
(1)FileOutputStream 用來創建磁盤文件的輸出流對象,通過 FileOutputStream 的構造函數來制定文件路徑和文件名。 創建 FileOutputStream 實例對象時,指定的文件應該是存在的和可寫的,並且沒有被其它應用程序佔用的。對於一個磁盤文件創建 FileOutputStream 對象有以下方式:
1、 FileOutputStream out = new FileOutputStream ("fileName");
2、
FileOutputStream
out =
new
FileOutputStream
("fileName", true); //是否追加
3、 File f = new File("fileName");
FileOutputStream out = new FileOutputStream (f);
FileOutputStream out = new FileOutputStream (f ,true);//是否追加
內存緩衝區的作用: 在應用程序和 I/O 設備之間,通常會有內存緩衝區。這是由於計算機訪問外部設備的速度,要比直接訪問內存的速度慢得多,如果在應用程序中的每一次 write 方法中都直接將數據寫入到外部設備中,cpu 就要花費更多的時間等待數據寫完。如果在程序中開闢了一個內存緩衝區,程序的每一次 write 方法的調用都是先將數據寫入到緩衝區中,只有緩衝區被填滿後,cpu 踩會將緩衝區的內容一次性的寫入到外部設備中。使用內存緩衝區有兩個方面的好處:1、提高了 cpu 的使用率。2、 write 方法並沒有真正寫入到外部設備,程序還有機會撤銷操作。對於輸入流也可以使用內存緩衝區,可以將外部大量數據讀取到內存緩衝區中,然後再從內存緩衝區中讀 取到應用程序中來。
(2)ByteArrayOutStream 參照 ByteArrayInputStream。
(3)DataOutputStream 參照 DataInputStream。
(4)BufferedOutputStream 參照 BufferedInputStream。
(8)ObjectOutputStream
參照 ObjectInputStream。
四、字符流
1、Reader 類
用於讀取字符流的抽象類,讀取文本格式的內容。Java 中的字符是採用 Unicode 編碼的,是雙字節的。而字節流中要讀取字符串時,我們需要將它轉換爲字節後再讀取數據。Java 中提供了單獨的字符流類。
二進制文件與文本文件的區別: 在不考慮正負數的情況下,每個字節中的數據在0~255之間 ,它們在內存中都是以 二進制的形式存在。文件就是內存中的數據複製到硬盤上的存儲形式,文件中的每個字節的數據也都是二進制形式的,所以嚴格的說磁盤上的每個文件都是二進制文 件。各種文本字符都是由一個或多個字節組成的,但是組成這些字符的字節不能是0~255之間的任意數,而是特定的數,有些數是不能存在於任何字符中的。如 果一個文件中所有每相鄰的字節數據都可以表示成一個字符,那我們就稱這個文件爲文本文件。文本文件只是二進制文件的一種特例,爲了與文本文件區別,人們又 把除了文本文件以外的文件稱爲二進制文件。
(1)FileReader 用來讀取字符文件的便捷類。
(2)InputStreamReader 用來讀取磁盤數據的便捷類。是字節流通向字符流的橋樑
編程舉例: 用 FileWriter 類向文件中寫入一個字符串,然後用 FileReader 獨處寫入的內容。
(3)PipedReader 和 PipedWriter 對照 PipedInputStream 和 PipedOutputStream
(4)StringReader 以字符 IO 流的方式處理字符串
(5)BufferedReader
參照 BufferedInputStream。
2、Writer 類
用於寫入字符流的抽象類,寫入文本格式的內容。
(1)FileWriter 用來寫入字符文件的便捷類。
(2)OutStreamWriter 用來寫入磁盤數據的便捷類。是字節流通向字符流的橋樑
(3)StingWriter 參照 StringReader。
(4)BufferedWriter 參照 BufferedOutputtStream。
五、字節流與字符流之間的轉換
- 能不能找到一種簡單的方式來讀取鍵盤上輸入的一行字符?我們很容易的想到 BufferedReader 中有一個 readLine() 的方法,但是 readLine() 只能接收 Reader 類型的字符流,而鍵盤的 System.in 屬於字節流,上面曾經說過,InputStreamReader 和 OutputStreamWriter 是字節流與字符流之間的橋樑,所以我們想到了用 InputStreamReader 來包裝 InputStream 類型的流。代碼:
InputStreamReader 和 OutputStreamWriter ,是用於將字節流轉換成字符流的兩個類, InpuStreamReader 可以將一個字節流中的字節解碼成字符後讀取,OutStreamWriter 將字符解碼成字節後寫入到一個字節流中。爲了避免繁瑣的在字節與字符之間轉換,最好不要直接使用 InputStreamReader 和 OutputStreamWriter ,應該儘量使用 BufferedWriter 和 BufferedReader 來包裝它們。
六、Java 程序與其它進程的數據通信
在 Java 中可以使用 Process 類的實例對象來表示子進程,子進程的標準輸入和輸出不再連接到鍵盤和顯示器,而是以管道流的形式連接到父進程的一個輸出流和輸入流對象上。調用 Process 類的 getOutputStream 和 getInputStream 方法可以獲得連接到子進程的輸出流和輸入流對象。
七、特殊的 RandomAccessFile 類
RandomAccessFile 類提供了衆多的文件訪問方法。 RandomAccessFile 類支持隨機訪問方式,也就是可以跳轉到文件的任意位置開始讀寫數據。 RandomAccessFile 實例對象中有個指示器,它可以跳轉到文件任意位置 。 RandomAccessFile 讀寫操作都是從指示器所指示的當前位置開始讀寫,當讀寫 N 個字節以後,文件指示器將指向 N 個字節後的下一個字節處。 RandomAccessFile 類在隨機(相對於順序讀取而言)讀寫等長記錄格式的文件時有恨大的優勢。 RandomAccessFile 類僅限於操作文件,不能訪問其它的 IO 設備。
RandomAccessFile 類兩種構造方法:
1、new RandomAccessFile(file,"rw"); //讀寫方式
2、new RandomAccessFile(file,"r"); //只讀方式
八、總結
字節流輸入流(InputStream)
FileInputStream、 ObjectInputStream、 PipedInputStream、 ByteArrayInputStream、 DataInputStream、 BufferedInputStream、 FilterInputStream
字節輸出流(OutputStream)
FileOutputStream、ObjectOutputStream、PipedOutputStream、ByteArrayOutputStream、DataOutputStream、BufferedOutputStream、FilterOutputStream、PrintStream
字符輸入流(Reader)
FileReader、CharArrayReader、StringReader、PipedReader、FilterReader、BufferedReader、InputStreamReader
字符輸出流(Writer)
FileWriter、 CharArrayWriter、 StringWriter、 PipedWriter、 FilterWriter、 BufferedWirter、 OutputStreamReader