I/O的基礎(chǔ)知識(shí)
流
Java 程序通過流執(zhí)行 I/O. 流是一種抽象, 要么產(chǎn)生信息, 要么使用信息. 流通過 Java 的 I/O 系統(tǒng)連接到物理設(shè)備.
所有流的行為方式都是相同的, 盡管與他們鏈接的物理設(shè)備是不同的.
因此, 可以為任意類型的設(shè)備應(yīng)用相同的 I/O 類和方法. 這意味著可以將許多不同類型的輸入-磁盤文件,鍵盤或網(wǎng)絡(luò) socket, 抽象為輸入流. 與之對(duì)應(yīng), 輸入流可以引用控制臺(tái),磁盤文件或網(wǎng)絡(luò)連接. 流是處理輸入/輸出的一種清晰方式.
字節(jié)流和字符流
Java 定義了兩種類型的流: 字節(jié)流額字符流.
字節(jié)流為處理字節(jié)的輸入和輸出提供了方法. 例如, 當(dāng)讀取和寫入二進(jìn)制數(shù)據(jù)時(shí), 使用的就是字節(jié)流.
字符流為處理字符的輸入和輸出提供了方便的方法. 它們使用 Unicode 編碼, 所以可以被國(guó)際化. 此外, 在某些情況下字符流比字節(jié)流高效.
另外一點(diǎn): 在最底層, 所有 I/O 仍然是面向字節(jié)的. 基于字符的流只是為處理字符提供了一種方便和高效的方法.
字節(jié)流
字節(jié)流是通過兩個(gè)類層次定義的. 在頂級(jí)是兩個(gè)抽象類: InputStream
和 OutputStream
.
每個(gè)抽象類都有幾個(gè)吃力何種不同設(shè)備的具體子類, 例如磁盤文件, 網(wǎng)絡(luò)連接甚至內(nèi)存緩沖區(qū).
下面列出了 java.io
包中的字符流類
流 類 | 含 義 |
---|---|
BufferedInputStream | 緩沖的輸入流 |
BufferedOutputStream | 緩沖的輸出流 |
ByteArrayInputStream | 讀取字節(jié)數(shù)組內(nèi)容的輸入流 |
ByteArrayOutputStream | 向字節(jié)數(shù)組寫入內(nèi)容的輸出流 |
DataInputStream | 包含讀取 Java 標(biāo)準(zhǔn)數(shù)據(jù)類型的方法的輸入流 |
DataOutputStream | 包含寫入 Java 標(biāo)準(zhǔn)數(shù)據(jù)類型的方法的輸出流 |
FileInputStream | 讀取文件內(nèi)容的輸入流 |
FileOnputStream | 向文件寫入內(nèi)容的輸出流 |
FilterInputStream | 實(shí)現(xiàn)InputStream |
FilterOutputStream | 實(shí)現(xiàn)OutputStream |
ObjectInputStream | 用于對(duì)象的輸入流 |
ObjectOutputStream | 用于對(duì)象的輸出流 |
PipedInputStream | 輸入管道 |
PipedOutputStream | 輸出管道 |
PrintStream | 包含print() 和 println() |
抽象類 InputStream
和 OutputStream
定義了其他流類實(shí)現(xiàn)的一些關(guān)鍵方法. 其中最重要的兩個(gè)方式 read()
和 write()
, 這兩個(gè)方法分別讀取和寫入字節(jié)數(shù)據(jù). 每個(gè)方法都有抽象形式, 派生的流類必須重寫這兩個(gè)方法.
字符流
字符流是通過兩個(gè)類層次定義的. 在頂層是兩個(gè)抽象類: Reader
和 Writer
. 這兩個(gè)抽象類處理 Unicode 字符流.
下面列出 java.io
包下的字符流類
流 類 | 含 義 |
---|---|
BufferedReader | 緩沖的輸入字符流 |
BufferedWriter | 緩沖的輸出字符流 |
CharArrayReader | 從字符數(shù)組讀取內(nèi)容的輸入流 |
CharArrayWriter | 從字符數(shù)組寫入內(nèi)容的輸出流 |
FileReader | 從文件讀取內(nèi)容的輸入流 |
FileWriter | 向文件寫入內(nèi)容的輸出流 |
FilterReader | 過濾的讀取器 |
FilterWriter | 過濾的寫入器 |
InputStreamReader | 將字節(jié)轉(zhuǎn)換成字符的輸入流 |
OutputStreamWriter | 將字符轉(zhuǎn)成成字節(jié)的輸出流 |
PipedReader | 輸入管道 |
PipedWriter | 輸出管道 |
PrintWriter | 包含 print() 和 println() |
StringReader | 從字符串讀取內(nèi)容的輸入流 |
StringWriter | 向字符串寫入內(nèi)容的輸出流 |
抽象類 Reader
和 Writer
定義了其它幾個(gè)流類實(shí)現(xiàn)的重要方法. 最重要的兩個(gè)方法是 read()
和 write()
, 這兩個(gè)方法分別讀取和寫入字符數(shù)據(jù). 每個(gè)方法都有抽象形式, 派生的流類必須實(shí)現(xiàn)這兩個(gè)方法.
PrintWriter 類
盡管使用 System.out
向控制臺(tái)輸出可以接受的, 但是最好將其用于調(diào)試. 對(duì)于實(shí)際的程序, 使用 java 向控制臺(tái)輸出的推薦方法是通過 PrintWriter
流.
PrintWriter 類定義了幾個(gè)構(gòu)造函數(shù), 在此將使用的構(gòu)造函數(shù)如下所示:
PrintWriter(OutputStream outputStream, boolean flushingOn)
其中, outputStream 是OutputStream 類型的對(duì)象, flushingOn 控制Java 是否在每次調(diào)用 println()
方法時(shí)刷新輸出流. 如果flushingOn 為true, 就自動(dòng)刷新; 如果為false, 那么不會(huì)自動(dòng)刷新.
PrintWriter 支持 print()
和 println()
方法. 因此, 可以使用與System.out 相同的方式使用它們. 如果參數(shù)不是簡(jiǎn)單類型, PrintWriter 方法會(huì)調(diào)用對(duì)象的 toString()
方法, 然后輸出結(jié)果.
為了使用 PrintWriter 向控制臺(tái)輸出, 為輸出流制定 System.out, 然后在每個(gè)新行之后刷新流.
public class PrintWriterDemo {
public static void main(String args[]) {
PrintWriter pw = new PrintWriter(System.out, true);
pw.println("This is a string");
int i = -7;
pw.println(i);
}
}
讀/寫文件
對(duì)于讀/寫文件, 兩個(gè)最常用的流是 FileInputStream
和 FileOutputStream
, 這兩個(gè)類創(chuàng)建與文件鏈接的字節(jié)流.
為了打開文件, 只需要?jiǎng)?chuàng)建簡(jiǎn)單的創(chuàng)建這些類中某個(gè)類的對(duì)象, 指定文件名作為構(gòu)造函數(shù)的參數(shù)即可.
FileInputStream(String fileName)
FileOutputStream(String fileName)
文件使用完之后必須關(guān)閉. 關(guān)閉文件是通過 close()
方法完成的, FileInputStream
和 FileOutputStream
都實(shí)現(xiàn)了該方法.
void close()
關(guān)閉文件會(huì)釋放文件分配的系統(tǒng)資源, 從而允許其他的文件使用這些資源. 關(guān)閉文件失敗會(huì)導(dǎo)致"內(nèi)存泄漏", 因?yàn)槲词褂玫馁Y源沒有被釋放.
注意: 從 JDK 7 開始,
close()
方法是由java.lang
包中的AutoCloseable
接口指定的.java.io
包中的 Closeable 接口竭誠(chéng)了AutoCloseable接口. 所有流類都實(shí)現(xiàn)了這兩個(gè)接口, 包括FileInputStream
和FileOutputStream
.
可以使用兩種方法關(guān)閉文件, 第一種是傳統(tǒng)方法, 當(dāng)不在需要文件時(shí)顯式調(diào)用close()方法. 第二種方式是使用帶資源的try語句, 這種try語句是由JDK 7 新增的, 當(dāng)不再需要文件時(shí)能夠自動(dòng)關(guān)閉文件.
try (FileInputStream fin = new FileInputStream(args[0]);
FileOutputStream fout = new FileOutputStream(args[1])) {
// ...
只有對(duì)于那些實(shí)現(xiàn)了
AotoCloseable
接口的資源, 才能使用帶資源的 try 語句.
下面是關(guān)于帶資源的 try 語句的3個(gè)關(guān)鍵點(diǎn):
- 帶資源的 try 語句管理的資源必須是實(shí)現(xiàn)了
AutoCloseable
接口的類的對(duì)象. - 在 try 代碼中聲明的資源被隱式聲明為
final
- 通過使用分號(hào)分隔每個(gè)聲明可以管理多個(gè)資源.
此外請(qǐng)記住, 所聲明資源的作用域被限制在帶資源的 try 語句中.
帶資源的 try 語句的主要優(yōu)點(diǎn)是: 當(dāng) try 代碼塊結(jié)束時(shí), 資源會(huì)被自動(dòng)關(guān)閉. 因此, 不可能會(huì)忘記關(guān)閉流. 使用帶資源的 try 語句, 通趁镂瑁可以時(shí)源代碼更短, 更清晰, 更容易維護(hù).