第一章 I / O基礎(chǔ)知識(shí)和API
輸入和輸出(I/O)設(shè)施是操作系統(tǒng)以及計(jì)算機(jī)語言及其庫的基礎(chǔ)部分。除了微不足道的計(jì)算機(jī)程序之外,所有這些程序都執(zhí)行某種輸入或輸出操作。
Java始終支持I/O。其初始的I/O和API相關(guān)架構(gòu)被稱為經(jīng)典I/O。由于現(xiàn)在操作系統(tǒng)具有經(jīng)典I/O不支持的較新的I/O特征,因此Java將新的I/O(NIO)作為JDK 1.4的一部分進(jìn)行了支持。由于時(shí)間關(guān)系導(dǎo)致一部分NIO功能沒有被包含在JDK 1.4中,因此其他的NIO功能被推遲到了JDK 5和JDK 7中實(shí)現(xiàn)了。
本章向您介紹了經(jīng)典的I/O,NIO和NIO.2。您將了解其所涉及到的基本I/O功能。此外,您將看到他們的API概述。后續(xù)章節(jié)中會(huì)深入了解這些API的功能。
1.1 經(jīng)典I/O
JDK 1.0引入了用于訪問文件系統(tǒng)的基本I/O功能(創(chuàng)建目錄,刪除文件或執(zhí)行其他任務(wù)),隨機(jī)訪問文件內(nèi)容(而不是依次訪問),并在源和目標(biāo)之間以順序的方式傳輸面向字節(jié)的數(shù)據(jù) 。
1.1.1 文件系統(tǒng)訪問和File類
文件系統(tǒng)是管理數(shù)據(jù)存儲(chǔ)和后續(xù)檢索的操作系統(tǒng)組件。運(yùn)行Java虛擬機(jī)(JVM)的操作系統(tǒng)至少支持一個(gè)文件系統(tǒng)。例如,Unix或Linux將所有已安裝(已連接和準(zhǔn)備好的)磁盤組合到一個(gè)虛擬文件系統(tǒng)中。 相比之下,Windows將單獨(dú)的文件系統(tǒng)與每個(gè)活動(dòng)磁盤驅(qū)動(dòng)器相關(guān)聯(lián)。
文件系統(tǒng)將存有數(shù)據(jù)的文件存儲(chǔ)在目錄中, 文件和目錄對(duì)象可以通過指定的路徑來訪問。路徑是定位文件和標(biāo)識(shí)文件系統(tǒng)對(duì)象的關(guān)聯(lián)映射,路徑可以是絕對(duì)的和相對(duì)的:
- 絕對(duì)路徑是相對(duì)于文件系統(tǒng)根目錄的路徑。 它表示為根目錄符號(hào),后跟目標(biāo)名稱的分隔層次結(jié)構(gòu),以目標(biāo)目錄或文件名結(jié)尾。
- 相對(duì)路徑是相對(duì)于某個(gè)其他目錄的路徑。它與絕對(duì)路徑類似,但沒有初始根目錄符號(hào)。 相比之下,它通常以一個(gè)或多個(gè)分隔的“..”字符序列為前綴,其中每個(gè)序列都指父目錄。
不同的操作系統(tǒng)路徑的表示方式不同。例如,Unix,Linux 和類Unix操作系統(tǒng)中使用正斜杠(/)標(biāo)識(shí)根目錄和路徑分隔組件,而在Windows中則使用反斜杠(\)。如下兩個(gè)例子:
# Unix,Linux和類Unix系統(tǒng)中的表示法
/users/username/bin
# Windows系統(tǒng)中的表示法
\users\username\bin
上面兩個(gè)例子分別是在Unix/Linux環(huán)境中訪問子目錄bin和在Windows中訪問子目錄bin的方式。
Windows和類似的操作系統(tǒng)可以管理多個(gè)文件系統(tǒng)。每個(gè)文件系統(tǒng)都使用驅(qū)動(dòng)器說明符(如“C:”)進(jìn)行標(biāo)識(shí)。 當(dāng)沒有指定驅(qū)動(dòng)器說明符的路徑時(shí),該路徑是相對(duì)于當(dāng)前文件系統(tǒng)。 否則,它是相對(duì)于指定的文件系統(tǒng) :
\users\username\bin
C:\users\username\bin
第一行訪問相對(duì)于當(dāng)前文件系統(tǒng)的路徑,而第二行訪問相對(duì)于C:文件系統(tǒng)的路徑 。
java.io.File類的一個(gè)實(shí)例抽象出文件或目錄路徑。 此實(shí)例提供對(duì)文件系統(tǒng)的訪問,以執(zhí)行此路徑上的任務(wù),例如刪除底層文件或目錄。 以下示例演示此類:
new File("temp").mkdir();
該示例構(gòu)造了一個(gè)初始化為文件系統(tǒng)對(duì)象temp的File對(duì)象。 然后在此File對(duì)象上調(diào)用mkdir()來創(chuàng)建一個(gè)名為temp的新目錄。
關(guān)于File類我們將放在第二章中深入探討。
1.1.2 通過RandomAccessFile訪問文件內(nèi)容
文件內(nèi)容可以順序或隨機(jī)訪問。 隨機(jī)訪問可以加快搜索和排序功能。 java.io.RandomAccessFile類的一個(gè)實(shí)例提供對(duì)文件的隨機(jī)訪問。 此功能在以下示例中進(jìn)行了說明:
RandomAccessFile raf=new RandomAccessFile("employees.dat","r");
int empIndex=10;
raf.seek(empIndex*EMP_REC_LEN);
//讀取員工記錄的內(nèi)容
在此示例中,文件employees.dat被放置了固定長(zhǎng)度的員工記錄,其中每條記錄的字節(jié)長(zhǎng)度為EMP_REC_LEN。以上正在查找的是索引為10(第一個(gè)記錄位于索引0)的員工記錄。該任務(wù)通過尋找(設(shè)置文件指針)到該記錄的第一個(gè)字節(jié)的位置,該位置位于索引乘以記錄長(zhǎng)度,然后就可以訪問到該記錄了。
第3章探討了RandomAccessFile類
1.1.3 通過流類流式傳輸數(shù)據(jù)
經(jīng)典I/O包括用于執(zhí)行I/O操作的流。字節(jié)流是有序的任意長(zhǎng)度的字節(jié)序列。字節(jié)流從應(yīng)用程序的輸出流流出到目的地,并從源中的輸入流流入到應(yīng)用程序。圖1-1說明了這些流程:
Java在java.io包中提供了各種對(duì)目標(biāo)地址寫入的類;例如,字節(jié)數(shù)組和文件。Java還在該包中提供了用于標(biāo)識(shí)從源中讀入的類。示例中包含文件和線程管道。
例如,您將使用FileInputStream打開現(xiàn)有文件并將輸入流連接到該文件。然后,您將調(diào)用各種read()方法通過輸入流讀取文件中的字節(jié)。 最后,你可以調(diào)用close()關(guān)閉流和文件。 請(qǐng)考慮以下示例 :
FileInputStream fis=null;
try{
fis=new FileInputStream("image.jpg");
//Read bytes from file
int _byte;
while((_byte=fis.read())!=-1){
//Handle your logic
}
}catch(IOException e){
//Handle Exception
}finally{
if (fis!=null) {
try {
fis.close();
}
}
}
此示例演示了打開文件并創(chuàng)建用于從文件讀取字節(jié)的輸入流的傳統(tǒng)方式。然后就可以繼續(xù)閱讀文件的內(nèi)容了。同時(shí)需要處理由java.io.IOException拋出的所有異常。
無論是否拋出異常,都必須關(guān)閉輸入流和目標(biāo)文件。這個(gè)動(dòng)作放在finally語句塊中進(jìn)行。由于關(guān)閉資源的冗長(zhǎng)性,您可以使用JDK 7中的try-with-resources語句自動(dòng)關(guān)閉它,如下所示 :
try(FileInputStream fis=new FileInputStream("image.jpg")) {
//Read bytes from file
int _byte;
while((_byte=fis.read())!=-1){
//Handle your logic
}
}catch (IOException e) {
//Handle Exception
}
關(guān)于傳統(tǒng)try-catch-finally和try-with-resources方法的區(qū)別我們將在后續(xù)章節(jié)中詳細(xì)講解。
一些流類用于過濾其他流類。例如,為了提高性能,BufferedInputStream從另一個(gè)流中讀取字節(jié)塊,再從緩沖區(qū)讀取返回字節(jié),直到緩沖區(qū)為空的時(shí)候,繼續(xù)讀取下一個(gè)字節(jié)塊,如下示例所示:
try(FileInputStream fis=new FileInputStream("image.jpg");
BufferedInputStream bis=new BufferedInputStream(fis)) {
//Read bytes from file
int _byte;
while((_byte=bis.read())!=-1){
//Handle your logic
}
}catch (IOException e) {
//Handle Exception
}
創(chuàng)建從image.jpg文件讀取的文件輸入流。 該流被傳遞到緩沖的輸入流構(gòu)造器。 在緩沖的輸入流上執(zhí)行后續(xù)讀取,在適當(dāng)時(shí)調(diào)用文件輸入流read()方法 。
第4章探討流類
1.1.4 流類和標(biāo)準(zhǔn)I / O
許多操作系統(tǒng)支持標(biāo)準(zhǔn)I / O,它是在計(jì)算機(jī)程序及其環(huán)境開始執(zhí)行時(shí)的預(yù)先連接的輸入和輸出數(shù)據(jù)流。 預(yù)先連接的流稱為標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤。
標(biāo)準(zhǔn)輸入默認(rèn)從鍵盤讀取其輸入。 此外,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤默認(rèn)將其輸出寫入屏幕。 然而,這些流可以被重定向以從不同的源讀取輸入并將輸出寫入到不同的目的地(例如文件)。
JDK 1.0通過將InputStream和PrintStream類型的in,out和err對(duì)象添加到j(luò)ava.lang.System類來引入對(duì)標(biāo)準(zhǔn)I / O的支持。 您可以在這些對(duì)象上指定方法調(diào)用來訪問標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤,如下所示:
int ch=System.in.read(); //從標(biāo)準(zhǔn)輸入讀取一個(gè)單個(gè)字符
System.out.println("Hello"); //使用標(biāo)準(zhǔn)輸出寫字符串
System.err.println("I/O error: "+ioe.getMessage()); //使用標(biāo)準(zhǔn)錯(cuò)誤寫字符串
除了探索InputStream和PrintStream,第4章也重新解讀
標(biāo)準(zhǔn)I / O,讓您了解如何以編程方式重定向這些流。
1.1.4.1 JDK 1.1和Writer/Reader類
JDK 1.0的I / O功能適用于流字節(jié),但由于不考慮字符編碼,因此無法正確流式傳輸字符。 JDK 1.1通過引入考慮到字符編碼的writer / reader類來克服這個(gè)問題。 例如,java.io包中包含用于寫入和讀取字符流的FileWriter和FileReader類。
第5章中探討各種Writer和Reader類