版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第16章資料輸入與輸出1第16章資料輸入與輸出1本章提要16-1甚麼是串流?16-2Java串流類別架構(gòu)16-3輸出、輸入資料16-4物件的讀寫16-5綜合演練2本章提要16-1甚麼是串流?2前言在本章之前,我們已多次用"importjava.io.*"敘述匯入Java的I/O(資料輸入與輸出)套件,並使用其中的BufferedReader類別的readLine()方法從鍵盤讀取使用者輸入的資料,以及用System.out.println()方法在螢?zāi)簧巷@示訊息或輸出程式執(zhí)行的結(jié)果。3前言在本章之前,我們已多次用"importjava.i前言但java.io套件的功能可不僅止於此,舉凡從電腦的螢?zāi)?、鍵盤等各種裝置輸出或輸入資料,或是讀寫電腦中的文字檔、二元檔(binaryfile),甚至是讀寫ZIP格式的壓縮檔,都可透過java.io套件中的類別來完成。本章就要來介紹Java的資料輸入與輸出架構(gòu),以及如何使用java.io套件的各項(xiàng)I/O類別。4前言但java.io套件的功能可不僅止於此,舉凡從電腦16-1甚麼是串流?為了簡(jiǎn)化程式設(shè)計(jì)人員處理I/O的動(dòng)作,不管讀取資料或?qū)懭胭Y料的來源/目的為何(檔案、網(wǎng)路、或記憶體等等),都是以串流(stream)的方式進(jìn)行資料的讀取與寫入。而串流就是形容資料像一條河流一樣,將資料依序從資料來源中流出,或是流入目的地中。516-1甚麼是串流?為了簡(jiǎn)化程式設(shè)計(jì)人員處理I/O的動(dòng)甚麼是串流?6甚麼是串流?6甚麼是串流?在java.io套件中,所有的資料輸出入類別都是以串流的方式來操作資料,不管讀取或?qū)懭?都離不開以下三個(gè)基本動(dòng)作:開啟串流(建構(gòu)串流物件)從串流讀取資料、或?qū)①Y料寫入串流關(guān)閉串流7甚麼是串流?在java.io套件中,所有的資料輸出入類甚麼是串流?從程式的觀點(diǎn),可供程式讀取的資料來源稱為輸入串流(inputstream);而可用來寫入資料的則稱為輸出串流(outputstream)。不管我們是從磁碟(檔案)、網(wǎng)路(URL)或其它來源或目的建立串流物件,讀寫的方式都相似,Java已替我們將其間的不同隱藏起來,讓我們可以用一致的方式來操作串流,大幅簡(jiǎn)化學(xué)習(xí)過程。8甚麼是串流?從程式的觀點(diǎn),可供程式讀取的資料來源稱為輸入串16-2Java串流類別架構(gòu)在java.io套件中,共有4組串流類別,這4組類別可分為兩大類:以byte為處理單位的輸出入串流,又可稱之為位元串流(ByteStreams)以char為處理單位的輸出入串流,又可稱之為字元串流(CharacterStreams)916-2Java串流類別架構(gòu)在java.io套件中,位元串流位元串流是以8位元的byte為單位進(jìn)行資料的讀寫,位元串流有兩個(gè)最上層的抽像類別:InputStream(輸入)及OutputStream(輸出)。所有的輸出入位元串流都是由這兩個(gè)類別衍生出來的,例如我們已用過很多次的System.out,它是個(gè)java.io.PrintStream類別的物件,此類別是FilterOutputStream的子類別,而FilterOutputStream則是OutputStream的子類別。10位元串流位元串流是以8位元的byte為單位進(jìn)行資料的位元串流關(guān)於位元串流的主要類別,請(qǐng)參見以下的類別圖:11位元串流關(guān)於位元串流的主要類別,請(qǐng)參見以下的類別圖:11位元串流12位元串流12位元串流每種類別都適合於某類的讀取或?qū)懭氲膭?dòng)作,例如ByteArrayInputStream適用於讀取位元陣列;FileOutputStream則適用於寫入檔案。另外比較特別的是ObjectIntputStream和ObjectOutputStream,這兩個(gè)串流類別是特別為了讀寫我們自訂類別的物件而設(shè)計(jì),其詳細(xì)用法會(huì)在16-4節(jié)中介紹。13位元串流每種類別都適合於某類的讀取或?qū)懭氲膭?dòng)作,例如By位元串流這些串流類別的讀/寫方法都有個(gè)共通的特性,就是它們的原型宣告都註明"throwsIOException",所以使用這些方法時(shí),要記得用try/catch來執(zhí)行,或是在您的方法宣告也加上"throwsIOException"的註記,將例外拋給上層。14位元串流這些串流類別的讀/寫方法都有個(gè)共通的特性,就是它字元串流字元串流是以16位元的char為單位進(jìn)行資料的讀寫,字元串流同樣有兩個(gè)最上層的抽像類別Reader、Writer,分別對(duì)應(yīng)於位元串流的InputStream、OutputStream。這類串流類別主要是因應(yīng)國際化的趨勢(shì),為方便處理16位元的Unicode字元而設(shè)的,而且字元串流也會(huì)自動(dòng)分辦資料中的8位元ASCII字元和Unicode字元,不會(huì)將兩種資料弄混。15字元串流字元串流是以16位元的char為單位進(jìn)行資料字元串流字元串流類別的架構(gòu)和位元串流有些類似,而且其中各類別、方法的用法也都和位元串流中對(duì)應(yīng)的類別、方法相似,所以學(xué)會(huì)一種用法就等於學(xué)會(huì)兩種。不過Reader、Writer的衍生類別數(shù)量較少:16字元串流字元串流類別的架構(gòu)和位元串流有些類似,而且其中各類字元串流17字元串流17字元串流18字元串流1816-3輸出、輸入資料標(biāo)準(zhǔn)輸出、輸入檔案輸出、輸入讀寫二元檔1916-3輸出、輸入資料標(biāo)準(zhǔn)輸出、輸入19標(biāo)準(zhǔn)輸出、輸入所謂標(biāo)準(zhǔn)輸出一般就是指螢?zāi)?而標(biāo)準(zhǔn)輸入則是指鍵盤,在前幾章的程式中,就是從鍵盤取得使用者輸入的資料,從螢?zāi)惠敵鲇嵪⒓皥?zhí)行結(jié)果。20標(biāo)準(zhǔn)輸出、輸入所謂標(biāo)準(zhǔn)輸出一般就是指螢?zāi)?而標(biāo)準(zhǔn)輸入則是指標(biāo)準(zhǔn)輸出在System類別中,有兩個(gè)PrintStream類別的成員:out成員:代表標(biāo)準(zhǔn)輸出裝置,一般而言,都是指電腦螢?zāi)弧2贿^我們可以利用轉(zhuǎn)向的方式,讓輸出的內(nèi)容是輸出到檔案、印表機(jī)、或遠(yuǎn)端的終端機(jī)等等。例如在命令提示字元視窗中,可以用“dir>test”的方式,使dir原本會(huì)顯示在螢?zāi)簧系馁Y訊『轉(zhuǎn)向』存到"test"這個(gè)檔案中。(在Unix/Linux系統(tǒng)下也可用相同的轉(zhuǎn)向技巧,例如"ls>test")。21標(biāo)準(zhǔn)輸出在System類別中,有兩個(gè)Prin標(biāo)準(zhǔn)輸出err成員:代表標(biāo)準(zhǔn)『示誤訊息』輸出裝置,同樣預(yù)設(shè)為螢?zāi)弧R酝?dāng)應(yīng)用程式執(zhí)行過程中遇到錯(cuò)誤並需顯示相關(guān)訊息通知使用者,此時(shí)就是將訊息輸出到此裝置。雖然err與out同樣預(yù)設(shè)為螢?zāi)?但我們將out轉(zhuǎn)向時(shí),err並不會(huì)跟著轉(zhuǎn)向。22標(biāo)準(zhǔn)輸出err成員:代表標(biāo)準(zhǔn)『示誤訊息』輸出裝置,同樣預(yù)標(biāo)準(zhǔn)輸出舉例來說,如果執(zhí)行"dirABC>test"這個(gè)命令,但資料夾中並無ABC這個(gè)檔案,此時(shí)dir指令仍會(huì)將"找不到檔案"的示誤訊息顯示在螢?zāi)簧?而不會(huì)存到test檔案中。23標(biāo)準(zhǔn)輸出舉例來說,如果執(zhí)行"dirABC>test標(biāo)準(zhǔn)輸出PrintStream類別多重定義了適用於Java各種資料型別的print()、println()方法(後者會(huì)在輸出資料後再多輸出一個(gè)換行字元以進(jìn)行換行),所以我們能用這兩個(gè)方法輸出任何資料型別,Java都會(huì)自動(dòng)以適當(dāng)?shù)母袷捷敵觥?4標(biāo)準(zhǔn)輸出PrintStream類別多重定義了適用於Jav標(biāo)準(zhǔn)輸出此外,PrintStream類別還有一對(duì)多重定義的write()方法,其功能也是輸出位元資料,但此時(shí)參數(shù)是資料的『位元值』。例如我們要輸出"A"這個(gè)字元,必須指定其ASCII碼65,例如"write(65);"。另一個(gè)write()方法則是可輸出位元陣列的元素,且可指定要從第幾個(gè)元素開始輸出、共輸出幾個(gè)元素:25標(biāo)準(zhǔn)輸出此外,PrintStream類別還有一對(duì)多重定義標(biāo)準(zhǔn)輸出PrintStream類別有個(gè)和其它串流類別不同的特點(diǎn),就是它的方法都不會(huì)拋出IOException例外。以下這個(gè)簡(jiǎn)單的程式示範(fàn)了這幾個(gè)方法的用法及效果:26標(biāo)準(zhǔn)輸出26標(biāo)準(zhǔn)輸出27標(biāo)準(zhǔn)輸出27標(biāo)準(zhǔn)輸出28標(biāo)準(zhǔn)輸出28標(biāo)準(zhǔn)輸出第8?12行的迴圈會(huì)分別用print()和write()方法輸出a[]陣列中的元素。print()方法會(huì)將各元素當(dāng)整數(shù)值輸出,所以可正常看到輸出值;使用write()方法時(shí)則是將元素值當(dāng)成一個(gè)2進(jìn)元數(shù)值輸出,對(duì)螢?zāi)欢?就是將元素值當(dāng)成ASCII碼,然後輸出對(duì)應(yīng)的ASCII字元。29標(biāo)準(zhǔn)輸出第8?12行的迴圈會(huì)分別用print()和標(biāo)準(zhǔn)輸出以a[0]為例,ASCII碼10是換行字元,所以輸出這行後會(huì)自動(dòng)換行;至於ASCII碼20對(duì)應(yīng)的字元?jiǎng)t是一個(gè)特殊的控制字元,所以a[1]這行後面看不到內(nèi)容;至於最後一個(gè)a[4]:160對(duì)應(yīng)的字碼超出127(該字元是a上面多一撇),所以在中文環(huán)境被當(dāng)成Big-5字碼第一碼,但因?yàn)闊o第二碼,因此只輸出一個(gè)問號(hào)。30標(biāo)準(zhǔn)輸出以a[0]為例,ASCII碼10是換行字標(biāo)準(zhǔn)輸出第14行改用err物件以write()方法輸出b陣列的全部?jī)?nèi)容。由於ASCII碼7是個(gè)特殊的BEL字元,它只會(huì)讓電腦發(fā)出嗶聲,但不會(huì)輸出任何『字』,而ASCII碼32對(duì)應(yīng)的是『空白』字元,所以這行敘述只會(huì)讓電腦發(fā)出三聲嗶聲,但螢?zāi)簧峡床坏饺魏屋敵觥?1標(biāo)準(zhǔn)輸出第14行改用err物件以write()方標(biāo)準(zhǔn)輸出若要測(cè)試System.out、System.err的差異,可改以轉(zhuǎn)向的方式來執(zhí)行這個(gè)範(fàn)例程式,例如:32標(biāo)準(zhǔn)輸出若要測(cè)試System.out、System.err標(biāo)準(zhǔn)輸入標(biāo)準(zhǔn)輸入一般指的是鍵盤,但同樣可以利用轉(zhuǎn)向的方式從其它裝置來取得。不過細(xì)心的讀者或許發(fā)現(xiàn),前幾章的範(fàn)例程式並未直接用System.in這個(gè)物件來讀取鍵盤輸入,我們都是另外建立一個(gè)BufferedReader類別的物件,然後用這個(gè)物件來讀取鍵盤輸入。為什麼要這樣做呢?原因很簡(jiǎn)單:就是為了方便處理。33標(biāo)準(zhǔn)輸入標(biāo)準(zhǔn)輸入一般指的是鍵盤,但同樣可以利用轉(zhuǎn)向的方式從標(biāo)準(zhǔn)輸入System.in這個(gè)成員是InputStream類別的物件,換言之它是將標(biāo)準(zhǔn)輸入當(dāng)成位元串流來處理,所以我們?nèi)粲盟鼇碜x取鍵盤輸入,讀到的都是位元的形式,處理上並不方便(想一下如果要讀取中文或Unicode字元,就需進(jìn)行額外的處理)。此外直接讀取鍵盤輸入串流時(shí),由於電腦鍵盤緩衝區(qū)的運(yùn)作方式,會(huì)造成一些不易處理的狀況。34標(biāo)準(zhǔn)輸入System.in這個(gè)成員是InputStrea標(biāo)準(zhǔn)輸入為讓讀者瞭解直接使用System.in的情況,我們先介紹InputStream類別的read()方法:35標(biāo)準(zhǔn)輸入為讓讀者瞭解直接使用System.in的情況,標(biāo)準(zhǔn)輸入使用這些方法時(shí),都需處理IOException例外,或是單純拋給上層處理。我們就來看一下透過System.in物件用這些方法直接讀取鍵盤輸入的情形:36標(biāo)準(zhǔn)輸入使用這些方法時(shí),都需處理IOException標(biāo)準(zhǔn)輸入37標(biāo)準(zhǔn)輸入37標(biāo)準(zhǔn)輸入38標(biāo)準(zhǔn)輸入38標(biāo)準(zhǔn)輸入39標(biāo)準(zhǔn)輸入39標(biāo)準(zhǔn)輸入第9、18行分別用不同的read()方法讀取鍵盤輸入的位元資料。第10行叫用Character.toString()方法(參見第17章)將字元轉(zhuǎn)成字串。第13、22行用Math.pow()方法(參見第17章)計(jì)算2的N次方。40標(biāo)準(zhǔn)輸入第9、18行分別用不同的read()方法讀取標(biāo)準(zhǔn)輸入讀者可能會(huì)覺得很奇怪,為何會(huì)有如上的執(zhí)行結(jié)果?最主要的原因是範(fàn)例程式第1次呼叫read()方法只讀取1個(gè)位元,但使用者可能輸入2位數(shù)字(多個(gè)位元)、且InputStream的read()方法也會(huì)讀到[Enter]按鍵的資訊所造成的。41標(biāo)準(zhǔn)輸入讀者可能會(huì)覺得很奇怪,為何會(huì)有如上的執(zhí)行結(jié)果?最主標(biāo)準(zhǔn)輸入回頭看第一個(gè)執(zhí)行結(jié)果:程式第1次要求輸入,我們輸入2時(shí),read()方法傳回的是“2”這個(gè)字元的ASCII碼,也就是50,所以程式必須進(jìn)行一些轉(zhuǎn)換,才能得到整數(shù)以進(jìn)行運(yùn)算。程式第2次要求輸入時(shí),我們還未輸入,程式就直接顯示例外訊息而結(jié)束,這是因?yàn)榍耙淮屋斎?時(shí)按下的[Enter]鍵會(huì)產(chǎn)生歸位(CarriageReturn)及換行(LineFeed)字元(控制碼分別13及10)。42標(biāo)準(zhǔn)輸入回頭看第一個(gè)執(zhí)行結(jié)果:程式第1次要求輸入,我們標(biāo)準(zhǔn)輸入所以第2次讀取時(shí),read()方法便直接讀到這些字元,造成輸入的字串變成空字串,導(dǎo)致第19行程式進(jìn)行轉(zhuǎn)換時(shí)發(fā)生例外。至於第2個(gè)執(zhí)行結(jié)果,則是在第1次輸入時(shí),就故意輸入多個(gè)字元。結(jié)果第2次的read()方法就讀到前次未讀到的'5',所以就直接計(jì)算2的5次方。43標(biāo)準(zhǔn)輸入所以第2次讀取時(shí),read()方法便直接讀到標(biāo)準(zhǔn)輸入雖然[Enter]鍵的問題並非不能解決,但一來這樣做會(huì)讓程式多做額外的處理,二來大多數(shù)的應(yīng)用程式都是要求使用者輸入『字元』而非位元,所以我們會(huì)用字元串流來包裝System.in,達(dá)到簡(jiǎn)化處理的目的。44標(biāo)準(zhǔn)輸入雖然[Enter]鍵的問題並非不能解決,但一來用字元串流來包裝System.in為了方便我們從鍵盤取得資料,我們會(huì)以字元串流來包裝System.in這個(gè)位元串流,『包裝』(wrap)意指用System.in來建立字元串流的物件,所以對(duì)程式來說,它使用的是『字元』串流,而非原始的System.in『位元』串流。45用字元串流來包裝System.in為了方便我們從鍵盤取得資用字元串流來包裝System.in以前幾章取得鍵盤輸入的方式為例,我們都使用如下的程式:46用字元串流來包裝System.in以前幾章取得鍵盤輸入的方用字元串流來包裝System.in上述程式就是先將System.in物件先包裝成InputStreamReader物件,然後再包一層變成BufferedReader物件,最後才用此物件的readLine()方法來取得輸入。之所以要包兩層,主要原因可分為2點(diǎn):47用字元串流來包裝System.in上述程式就是先將Sys用字元串流來包裝System.inInputStreamReader是個(gè)特殊的字元串流,它的功用就是從位元串流取得輸入,然後將這些位元解讀成字元。因此在建構(gòu)InputStreamReader物件時(shí),必須以一個(gè)位元串流物件為參數(shù)來呼叫其建構(gòu)方法。但I(xiàn)nputStreamReader在使用上仍有前述[Enter]鍵的問題,操作並不方便。因此一般都會(huì)將它再包裝成其它更方便使用的串流類別物件,例如BufferedReader。48用字元串流來包裝System.inInputStreamR用字元串流來包裝System.inBufferedReader是所謂的緩衝式輸入串流,也就是先將串流的輸入存到一記憶體緩衝區(qū)中,程式再到這個(gè)緩衝區(qū)讀取輸入。在讀取檔案時(shí)這種緩衝式輸入效率較佳,而讀取鍵盤輸入時(shí),也可免去處理[Enter]鍵的問題。49用字元串流來包裝System.inBufferedRead用字元串流來包裝System.in但BufferedReader只有以Reader物件為參數(shù)的建構(gòu)方法,因此我們必須先將System.in轉(zhuǎn)成InputStreamReader物件,才能用後者呼叫BufferedReader的建構(gòu)方法,產(chǎn)生所要的物件。使用BufferedReader的readLine()方法讀取輸入時(shí),每次會(huì)讀取『一行』的內(nèi)容,且會(huì)自動(dòng)忽略該行結(jié)尾的歸位及換行字元,因此可順利解決[Enter]鍵的問題。請(qǐng)參考以下範(fàn)例:50用字元串流來包裝System.in但BufferedRe用字元串流來包裝System.in51用字元串流來包裝System.in51用字元串流來包裝System.in52用字元串流來包裝System.in52用字元串流來包裝System.in53用字元串流來包裝System.in53用字元串流來包裝System.in第09行用InputStreamReader包裝System.in。第14行以while迴圈的方式連續(xù)讀取多個(gè)字元,遇到換行字元(字碼為10)時(shí)即停止。第18、19行改以for迴圈輸出所有讀到的字元。第24行使用BufferedReader包裝第09行建立的InputStreamReader物件。54用字元串流來包裝System.in第09行用Inpu用字元串流來包裝System.in此外BufferedReader仍是有兩個(gè)read()方法可用於特定的字元讀取方式:55用字元串流來包裝System.in此外BufferedR檔案輸出、輸入在前一節(jié)我們透過System.in及System.out認(rèn)識(shí)一些位元串流及字元串流的基本用法。其實(shí)只要稍加變化,我們就能用串流來讀寫檔案了。如前所述,要進(jìn)行檔案讀寫,首先要做的就是開啟檔案串流,接著即可用串流的方法進(jìn)行讀寫,讀寫完畢後則需關(guān)閉串流以節(jié)省系統(tǒng)資源。56檔案輸出、輸入在前一節(jié)我們透過System.in及Sy使用字元串流讀取文字檔要讀寫檔案,可使用內(nèi)建的FileReader/FileWriter字元串流來處理,如其名稱所示,它們是專為檔案所設(shè)計(jì)的。這兩個(gè)字元串流的用法都很簡(jiǎn)單,分別以檔案名稱為參數(shù)呼叫其建構(gòu)方法即可建立該檔案的串流物件,以下我們先來看FileReader的用法。57使用字元串流讀取文字檔要讀寫檔案,可使用內(nèi)建的FileR使用字元串流讀取文字檔FileReader是InputStreamReader的子類別,所以可用前一節(jié)介紹的read()方法來讀取串流中的字元。以下就是用FileReader讀取文字檔中所有字元並輸出在螢?zāi)簧系男〕淌健?8使用字元串流讀取文字檔FileReader是InputS使用字元串流讀取文字檔59使用字元串流讀取文字檔59使用字元串流讀取文字檔60使用字元串流讀取文字檔60使用字元串流讀取文字檔61使用字元串流讀取文字檔61使用字元串流讀取文字檔第13行取得使用者輸入的檔名(路徑)字串,第14行即以此字串建立FileReader物件fr。第18、19行以while迴圈的方式連續(xù)用fr.read()讀取檔案中的字元,讀到檔案結(jié)尾時(shí),read()會(huì)傳回-1,即停止迴圈。第21行呼叫close()關(guān)閉檔案串流。62使用字元串流讀取文字檔第13行取得使用者輸入的檔名(路使用字元串流讀取文字檔至於寫入檔案用的FileWriter類別則是OutputStreamWriter的子類別。請(qǐng)注意,如果在建立寫入串流時(shí),指定了已存在的檔案,則程式會(huì)將檔案中原有的資料全部清除,再寫入新的資料。FileReader類別並無定義自己的寫入方法,其寫入功能只有繼承自O(shè)utputStreamWriter的三個(gè)write()方法:63使用字元串流讀取文字檔至於寫入檔案用的FileWriter使用字元串流讀取文字檔64使用字元串流讀取文字檔64使用字元串流讀取文字檔相信這3個(gè)write()的用法應(yīng)不必特別說明了,我們直接來看範(fàn)例程式的使用情形。以下這個(gè)範(fàn)例程式請(qǐng)使用者輸入新的檔案名稱,並建立FileReader寫入串流,接著請(qǐng)使用者輸入字串、整數(shù)、浮點(diǎn)數(shù)等三種資料,並寫入檔案串流中,最後並輸出檔案內(nèi)容以比對(duì)檢視:65使用字元串流讀取文字檔相信這3個(gè)write()的用法使用字元串流讀取文字檔66使用字元串流讀取文字檔66使用字元串流讀取文字檔67使用字元串流讀取文字檔67使用字元串流讀取文字檔68使用字元串流讀取文字檔68使用字元串流讀取文字檔69使用字元串流讀取文字檔69使用字元串流讀取文字檔第14行用使用者輸入的檔名路徑建立新的串流物件。雖然訊息提示的是請(qǐng)使用者輸入新檔案,但其實(shí)也可輸入現(xiàn)有的檔名,但此舉將會(huì)使檔案原有的內(nèi)容被範(fàn)例程式寫入的內(nèi)容覆蓋掉,因此建議不要用既有檔案做測(cè)試。第18、23、28行分別將使用者輸入的資料以字串的格式用write()方法寫入。70使用字元串流讀取文字檔第14行用使用者輸入的檔名路徑建立使用字元串流讀取文字檔第19、24行以write()寫入換行字元,模擬輸入[Enter]按鍵的效果。也就是讓輸入的三個(gè)字串會(huì)分別存在3行。若不加這幾行程式,寫入檔案的內(nèi)容,都會(huì)在同一行。第30行用flush()方法將所有未寫入的內(nèi)容立即寫入串流,然後才於31行用close()方法關(guān)閉檔案串流。71使用字元串流讀取文字檔第19、24行以write()使用字元串流讀取文字檔第33?37行另外建立FileReader物件讀取檔案內(nèi)容,並顯示在螢?zāi)簧?以檢查剛才的輸入及寫入是否正常。讀者可發(fā)現(xiàn),直接使用FileReader/FileWriter字元串流來處理檔案其實(shí)並不方便,簡(jiǎn)單如檔案換行的動(dòng)作也要我們自行用write()方法寫入個(gè)換行字元。72使用字元串流讀取文字檔第33?37行另外建立FileR使用字元串流讀取文字檔而且我們還只介紹文字檔的部份,若要處理二元檔案(binaryfile,例如圖形檔),顯然會(huì)遇到更多的不便。因此一般在處理檔案串流時(shí),也和使用System.in一樣,將檔案串流用較好用的緩衝式的串流包裝起來,以下就來介紹如何透過緩衝式串流來讀寫檔案。73使用字元串流讀取文字檔而且我們還只介紹文字檔的部份,若要處使用緩衝式串流包裝檔案串流讀取檔案時(shí),我們同樣可用BufferedReader來包裝FileReader物件,然後就能用readLine()方法來做整行的讀取。至於寫入方面,則可用對(duì)應(yīng)的BufferedWriter來包裝FileWriter物件,BufferedWriter除了有和FileWriter一樣的三個(gè)方法外,還多了一個(gè)newLine()方法可替我們進(jìn)行換行動(dòng)作。74使用緩衝式串流包裝檔案串流讀取檔案時(shí),我們同樣可用Buf效率較佳的緩衝式處理使用緩衝式串流來處理檔案讀寫還有一個(gè)優(yōu)點(diǎn),就是讀寫的效率會(huì)比較佳。如果直接以檔案串流讀寫檔案,程式每一個(gè)讀寫敘述,都會(huì)使系統(tǒng)進(jìn)行一次讀寫動(dòng)作;而使用緩衝式讀寫串流,可將一大筆資料都預(yù)先讀到緩衝區(qū)(記憶體空間),或是等要寫入的資料累積滿整個(gè)緩衝區(qū)時(shí)再一次寫入,如此程式的效能會(huì)稍有提昇。75效率較佳的緩衝式處理使用緩衝式串流來處理檔案讀寫還有一個(gè)優(yōu)點(diǎn)使用緩衝式串流包裝檔案串流使用緩衝式寫入串流BufferedWriter時(shí),一定要在關(guān)閉串流物件前,用flush()將緩衝區(qū)中的資料立即寫入串流,否則會(huì)造成關(guān)閉串流而仍有資料未寫入的情況。以下就是使用緩衝式串流讀寫檔案的範(fàn)例:76使用緩衝式串流包裝檔案串流使用緩衝式寫入串流Buffere使用緩衝式串流包裝檔案串流77使用緩衝式串流包裝檔案串流77使用緩衝式串流包裝檔案串流78使用緩衝式串流包裝檔案串流78使用緩衝式串流包裝檔案串流79使用緩衝式串流包裝檔案串流79使用緩衝式串流包裝檔案串流80使用緩衝式串流包裝檔案串流80使用緩衝式串流包裝檔案串流81使用緩衝式串流包裝檔案串流81使用緩衝式串流包裝檔案串流第14、15行用使用者輸入的檔名路徑建立新FileWriter串流物件,再用此物件建立BufferedWriter緩衝式字元寫入串流。和前一範(fàn)例相同,雖然訊息提示的是請(qǐng)使用者輸入新檔案,但其實(shí)也可輸入現(xiàn)有的檔名,但此舉將會(huì)使檔案原有的內(nèi)容被範(fàn)例程式寫入的內(nèi)容覆蓋掉。82使用緩衝式串流包裝檔案串流第14、15行用使用者輸入的檔使用緩衝式串流包裝檔案串流第22、28行分別以BufferedWriter的write()方法寫入使用者輸入的姓名和電話字串。第33行判斷使用者輸入的是否為大/小寫的"Y",是就再執(zhí)行一次迴圈,也就是再讓使用者輸入一筆資料。第35、36行將緩衝區(qū)內(nèi)容全部寫入,並關(guān)閉串流。83使用緩衝式串流包裝檔案串流第22、28行分別以Buff使用緩衝式串流包裝檔案串流第43?47行是建立BufferedReader串流物件以讀取檔案內(nèi)容,並顯示在螢?zāi)簧?。?5、46行利用while迴圈重複以BufferedReader的readLine()方法讀取檔案的每一行,當(dāng)讀到的字串為null時(shí),即表示已到檔案結(jié)尾。84使用緩衝式串流包裝檔案串流第43?47行是建立Buff使用緩衝式串流包裝檔案串流此例改用BufferedReader的readLine()方法來讀取檔案內(nèi)容,就不必像前幾個(gè)範(fàn)例程式一樣,用Reader類別的字元讀取方法read()來讀取了。85使用緩衝式串流包裝檔案串流此例改用BufferedRead讀寫二元檔文字檔可說是為了直接給人看而存在的,給電腦程式用的檔案其實(shí)使用二元檔(binaryfile)就可以了。以Java為例,早在第4章我們就學(xué)過Java的各種資料型別,這些資料型態(tài)就是可由程式直接取用的。如果連數(shù)字都存成字串型式"123456",那Java還要自己把它轉(zhuǎn)成整數(shù)或其它數(shù)值型別才能進(jìn)行運(yùn)算,非常不便。86讀寫二元檔文字檔可說是為了直接給人看而存在的,給電腦程式用讀寫二元檔所以如果存程式用的資料也能使用像資料型別的格式,顯然就比存成文字檔方便得多了。而要讓資料以有如資料型別的格式來儲(chǔ)存,就要使用二元檔。以"123456"為例,若是使用整數(shù)格式存放時(shí),其4個(gè)位元組的值是"0001E240"。如果我們看到這樣的檔案內(nèi)容,一定無法理解它們是什麼意思,所以說二元檔是『給程式(電腦)看的檔案』。87讀寫二元檔所以如果存程式用的資料也能使用像資料型別的格式,讀寫二元檔使用二元檔時(shí),由於很多資料都不是字元,所以通常是以位元串流來處理。在位元串流中,有FileInputStream和FileOutputStream兩個(gè)檔案輸入與輸出串流。但同樣的,直接用這兩個(gè)串流來讀寫檔案非常不便,因此我們通常會(huì)用DataInputStream、DataOutputStream這兩個(gè)位元串流包裝檔案串流,然後讀寫二元檔。88讀寫二元檔使用二元檔時(shí),由於很多資料都不是字元,所以通常讀寫二元檔這兩個(gè)類別的特別之處,就在於它們分別實(shí)作了java.io套件中DataInput、DataOutput這兩個(gè)介面。89讀寫二元檔這兩個(gè)類別的特別之處,就在於它們分別實(shí)作了jaDataOutputStreamDataOutput介面定義了一組寫入的方法,而DataOutputStream實(shí)作了這個(gè)介面,方便我們可直接寫入各種Java原生資料型別。只要呼叫這些方法,就能將資料以二元的方式寫入串流中。以下所列就是DataOutputStream的資料寫入方法:90DataOutputStreamDataOutput介面定DataOutputStream91DataOutputStream91DataOutputStream以下就是個(gè)簡(jiǎn)單的資料寫入程式:92DataOutputStream以下就是個(gè)簡(jiǎn)單的資料寫入程式DataOutputStream93DataOutputStream93DataOutputStream94DataOutputStream94DataOutputStream第14?17行以層層包裝的方式,建構(gòu)程式寫入檔案時(shí)所用的DataOutputStream物件。95DataOutputStream第14?17行以層層包DataOutputStream第30行呼叫DataOutputStream的size()方法傳回寫入的總位元數(shù),此數(shù)值應(yīng)和用"dir"命令所看到的檔案大小數(shù)字相同。第31、32行做最後的『清理』及關(guān)閉串流動(dòng)作。96DataOutputStream第30行呼叫DataODataOutputStream執(zhí)行此程式,輸入檔名後,程式就會(huì)將計(jì)算結(jié)果寫入指定的檔案中,並傳回寫入的位元組數(shù)。但因?yàn)槭且远獧n的格式儲(chǔ)存,所以我們無法用一般文字編輯器讀取其內(nèi)容,例如用我們先前寫的文字檔讀取程式來讀取程式寫入的檔案,只會(huì)看到如"1Aj?0Agg?"這些亂碼。97DataOutputStream執(zhí)行此程式,輸入檔名後,DataInputStream要解讀上述的二元檔案,當(dāng)然是以對(duì)應(yīng)的DataInputStream來處理最為方便。DataInputStream實(shí)作了DataInput介面,同理此介面定義了各種資料型別的讀取方法,透過DataInputStream物件呼叫這些現(xiàn)成的方法,即可輕鬆從串流讀取各種資料型別。這些方法的名稱也都很一致,幾乎是前述的writeXXX()方法改成readXXX()即可,例如:98DataInputStream要解讀上述的二元檔案,當(dāng)然是DataInputStream99DataInputStream99DataInputStream以下就是我們用DataInputStream讀取前一個(gè)程式所建立的二元檔的範(fàn)例程式:100DataInputStream以下就是我們用DataInpDataInputStream101DataInputStream101DataInputStream102DataInputStream102DataInputStream103DataInputStream103DataInputStream第14?17行以層層包裝的方式,建構(gòu)程式讀取檔案時(shí)所用的DataIuputStream物件。第21?29行以try的方式執(zhí)行讀取檔案及顯示資料的動(dòng)作。104DataInputStream第14?17行以層層包裝的DataInputStream第22?25行以while()迴圈持續(xù)讀取檔案,其中第23?24行分別以DataInputStream的readInt()、readDouble()方法來讀取檔案中的整數(shù)及浮點(diǎn)數(shù)資料。第27行呼叫DataInputStream的skipBytes()跳過12個(gè)位元組,使程式每讀一筆整數(shù)及浮點(diǎn)數(shù)資料,就跳過另一筆。因此只會(huì)顯示檔案中『第單數(shù)筆』的資料。105DataInputStream第22?25行以whilDataInputStream第30行的catch敘述捕捉EOFException檔案結(jié)束例外物件,並在第31行關(guān)閉串流。EOFException是IOException的衍生類別,用來表示已讀到檔案結(jié)尾(EndOfFile,EOF)或串流結(jié)尾的例外狀況。106DataInputStream第30行的catch敘無正負(fù)號(hào)的整數(shù)Java的整數(shù)型別都是可存放正負(fù)數(shù)值,但像C/C++程式語言都可宣告『無正負(fù)號(hào)』(unsigned)的整數(shù)。以16位元的short為例,"unsignedshort",可存放0?65535的數(shù)值,但Java的short因?yàn)橐惨鼙硎矩?fù)數(shù),所以只能表示-32768?32767的數(shù)值。107無正負(fù)號(hào)的整數(shù)Java的整數(shù)型別都是可存放正負(fù)數(shù)值,但像無正負(fù)號(hào)的整數(shù)為了讓Java程式也能正確讀寫由C/C++程式讀寫的這類資料,DataInputStream和DataOutputStream各有一對(duì)特別的讀寫方法,可讀寫無正負(fù)號(hào)的整數(shù)資料:108無正負(fù)號(hào)的整數(shù)為了讓Java程式也能正確讀寫由C/C+16-4物件的讀寫Java是物件導(dǎo)向的程式語言,因此很多情況我們會(huì)需要將物件的資料寫入檔案。如果使用前面學(xué)過的方法,以寫入二元檔為例,您必須呼叫DataOutputStream的多個(gè)writeXXX()方法,才能將物件中每個(gè)資料成員一一寫入串流。10916-4物件的讀寫Java是物件導(dǎo)向的程式語言,因此很物件的讀寫其實(shí)從本章開頭的類別架構(gòu)圖可發(fā)現(xiàn),在位元串流部分,Java已提供ObjectOutputStream、ObjectInputStream這兩個(gè)專用於物件讀寫的串流。它們各有readObject()、writeObject()方法可一次就讀取整個(gè)物件的資料,或?qū)懭胝麄€(gè)物件。110物件的讀寫其實(shí)從本章開頭的類別架構(gòu)圖可發(fā)現(xiàn),在位元串流部分實(shí)作Serializable介面但是,我們不能任意用這些串流及其方法來讀寫類別物件,必須有實(shí)作java.io套件中Serializable介面的類別,才能用ObjectXXX串流物件來讀寫其物件。所幸,實(shí)作Serializable介面是個(gè)相當(dāng)簡(jiǎn)單的動(dòng)作,因?yàn)檫@個(gè)介面未定義任何的方法和成員,所以我們只要在類別定義加上"implementsSerializable"這幾個(gè)字就可以了,完全不需再自訂任何方法。111實(shí)作Serializable介面但是,我們不能任意用這實(shí)作Serializable介面此外要讀寫物件還需注意一點(diǎn),因?yàn)镺bjectOutputStream在寫入物件時(shí),也會(huì)將類別的資訊記錄下來,所以若要用另一個(gè)程式以O(shè)bjectInputStream將物件讀回來,必須兩個(gè)程式中所定義的物件類別『完全』相同,不能只是有相同的資料成員,必須連方法及其它宣告也都一樣,否則程式在進(jìn)行讀取時(shí),會(huì)引發(fā)ClassNotFoundException(找不到類別)的例外。112實(shí)作Serializable介面此外要讀寫物件還需注意一寫入物件以下我們就沿用第14章TestCar.java這個(gè)範(fàn)例程式中的MyCar自訂類別,將它宣告為"implementsSerializable":113寫入物件以下我們就沿用第14章TestCar.java寫入物件114寫入物件114寫入物件115寫入物件115寫入物件接著我們就能寫一個(gè)程式,利用ObjectOutputStream串流物件將汽車物件寫到指定的檔案中。以下這個(gè)範(fàn)例程式會(huì)先請(qǐng)使用者輸入愛車的油量及耗油率資訊,並以之建立MyCar物件,然後再用ObjectOutputStream串流物件將之寫入mycar這個(gè)檔案。116寫入物件接著我們就能寫一個(gè)程式,利用ObjectOutp寫入物件117寫入物件117寫入物件118寫入物件118寫入物件119寫入物件119寫入物件第23、24行將FileOutputStream物件包裝成ObjectOutputStream物件。第26行以O(shè)bjectOutputStream的writeObject()方法將物件寫入串流中。第27、28行呼叫將串流中所有資料立即寫入並關(guān)閉串流。120寫入物件第23、24行將FileOutputStrea寫入物件若以一般文書編輯器開啟程式寫入的檔案"mycar",將會(huì)看到一團(tuán)亂碼,因?yàn)镺bjectOutputStream是以二元檔的方式將物件寫入檔案中,要讀回檔案中的物件資訊,可用ObjectInputStream串流。121寫入物件若以一般文書編輯器開啟程式寫入的檔案"mycar"從檔案讀取物件資料要將檔案(或其它串流)中的物件資料讀回程式中處理,可使用ObjectInputStream的readObject()方法。請(qǐng)?zhí)貏e注意,readObject()會(huì)拋出IOException、ClassNotFoundException這兩個(gè)Checked例外,所以在呼叫readObject()的方法中,必須拋出或處理這兩個(gè)例外。122從檔案讀取物件資料要將檔案(或其它串流)中的物件資料讀回從檔案讀取物件資料也因此在讀取物件的範(fàn)例程式中,必須比讀寫其它串流時(shí),多處理一個(gè)ClassNotFoundException例外。請(qǐng)參考以下的範(fàn)例程式:123從檔案讀取物件資料也因此在讀取物件的範(fàn)例程式中,必須比讀寫從檔案讀取物件資料124從檔案讀取物件資料124從檔案讀取物件資料125從檔案讀取物件資料125從檔案讀取物件資料126從檔案讀取物件資料126從檔案讀取物件資料第6行將main()方法宣告多拋出一個(gè)ClassNotFoundException例外。第09、10行將FileInputStream包裝成ObjectInputStream物件。127從檔案讀取物件資料第6行將main()方法宣告多拋出從檔案讀取物件資料第11行以O(shè)bjectInputStream的readObject()方法將從串流讀回物件。由於此程式只讀一筆物件就自行關(guān)閉迴圈,所以未用try/catch來執(zhí)行readObject()方法,若您要參考前幾個(gè)範(fàn)例程式的作法,讓程式一直讀到檔案結(jié)尾,就必須用try來執(zhí)行readObject()方法,並用catch捕捉EOFException例外物件。128從檔案讀取物件資料第11行以O(shè)bjectInputSt從檔案讀取物件資料第12行關(guān)閉串流。第17?30行則是模擬汽車行駛的迴圈,此部份的說明可參見第14章。129從檔案讀取物件資料第12行關(guān)閉串流。12916-5綜合演練將學(xué)生成績(jī)資料存檔讀取學(xué)生成績(jī)檔並計(jì)算平均13016-5綜合演練將學(xué)生成績(jī)資料存檔130將學(xué)生成績(jī)資料存檔在現(xiàn)實(shí)環(huán)境中,將物件資料存檔是很實(shí)際的應(yīng)用。本範(fàn)例程式就是建立一個(gè)學(xué)生成績(jī)資料類別,並提供輸入介面,最後再將輸入的學(xué)生成績(jī)物件存檔。為方便起見,我們先設(shè)計(jì)一個(gè)存放學(xué)生資料的Student類別,並存於Student.java檔案中。131將學(xué)生成績(jī)資料存檔在現(xiàn)實(shí)環(huán)境中,將物件資料存檔是很實(shí)際的應(yīng)將學(xué)生成績(jī)資料存檔132將學(xué)生成績(jī)資料存檔132將學(xué)生成績(jī)資料存檔133將學(xué)生成績(jī)資料存檔133將學(xué)生成績(jī)資料存檔第3行用"implementsSerializable"宣告此類別可寫入檔案中。第5?10行為可設(shè)定所有資料成員值的建構(gòu)方法。134將學(xué)生成績(jī)資料存檔第3行用"implementsSe將學(xué)生成績(jī)資料存檔第15?18行定義了4個(gè)可傳回物件中各成員值的方法。第21行定義了計(jì)算及傳回個(gè)人平均分?jǐn)?shù)的方法,在下個(gè)範(fàn)例中會(huì)用到。第25?28行分別宣告姓名及英文/數(shù)學(xué)/Java三科成績(jī)的資料成員。接下來我們就來設(shè)計(jì)一個(gè)程式,可讓使用者輸入學(xué)生資料,並將學(xué)生資料存檔。135將學(xué)生成績(jī)資料存檔第15?18行定義了4個(gè)可傳回物件將學(xué)生成績(jī)資料存檔136將學(xué)生成績(jī)資料存檔136將學(xué)生成績(jī)資料存檔137將學(xué)生成績(jī)資料存檔137將學(xué)生成績(jī)資料存檔138將學(xué)生成績(jī)資料存檔138將學(xué)生成績(jī)資料存檔139將學(xué)生成績(jī)資料存檔139將學(xué)生成績(jī)資料存檔140將學(xué)生成績(jī)資料存檔140將學(xué)生成績(jī)資料存檔第14、15行將FileOutputStream包裝成ObjectOutputStream物件。第18行宣告的counter變數(shù)是用來記錄使用者共輸入了幾筆學(xué)生資料。第20?44行以do/while迴圈持續(xù)取得使用者輸入的學(xué)生資料以建立學(xué)生物件,並於第40行以O(shè)bjectOutputStream的writeObject()方法將物件寫入串流中。141將學(xué)生成績(jī)資料存檔第14、15行將FileOutput將學(xué)生成績(jī)資料存檔第46、47行呼叫將串流中所有資料立即寫入並關(guān)閉串流。第49、50行顯示程式共寫入幾筆學(xué)生資料至檔案中。程式會(huì)一直請(qǐng)使用者輸入學(xué)生資料,直到使用者回答不再輸入為止。利用這個(gè)程式建立的學(xué)生資料檔也是二元檔,我們可用物件讀取串流來讀取這個(gè)檔案的內(nèi)容。142將學(xué)生成績(jī)資料存檔第46、47行呼叫將串流中所有資料立讀取學(xué)生成績(jī)檔並計(jì)算平均這個(gè)範(fàn)例是要讀取檔案中的學(xué)生成績(jī)並顯示出來,同時(shí)還會(huì)加總各科分?jǐn)?shù),以計(jì)算各科的平均分?jǐn)?shù)。此程式會(huì)用try/catch區(qū)塊來進(jìn)行讀取物件的動(dòng)作,try區(qū)塊中以while迴圈持續(xù)用ObjectInputStream的readObject()方法讀取物件,當(dāng)程式讀到檔案結(jié)尾時(shí),readObject()方法會(huì)拋出EOFException例外,此時(shí)程式即可計(jì)算總平均分?jǐn)?shù)並關(guān)閉串流。143讀取學(xué)生成績(jī)檔並計(jì)算平均這個(gè)範(fàn)例是要讀取檔案中的學(xué)生成績(jī)並顯讀取學(xué)生成績(jī)檔並計(jì)算平均144讀取學(xué)生成績(jī)檔並計(jì)算平均144讀取學(xué)生成績(jī)檔並計(jì)算平均145讀取學(xué)生成績(jī)檔並計(jì)算平均145讀取學(xué)生成績(jī)檔並計(jì)算平均146讀取學(xué)生成績(jī)檔並計(jì)算平均146讀取學(xué)生成績(jī)檔並計(jì)算平均147讀取學(xué)生成績(jī)檔並計(jì)算平均147讀取學(xué)生成績(jī)檔並計(jì)算平均148讀取學(xué)生成績(jī)檔並計(jì)算平均148讀取學(xué)生成績(jī)檔並計(jì)算平均第15、16行將FileInputStream物件包裝成ObjectInputStream物件。第18行宣告的counter變數(shù)是用來記錄共讀取了幾筆學(xué)生資料。第20?22行宣告三個(gè)變數(shù),以計(jì)算各科所有學(xué)生分?jǐn)?shù)的總和,以便算出各科的平均分?jǐn)?shù)。149讀取學(xué)生成績(jī)檔並計(jì)算平均第15、16行將FileInp讀取學(xué)生成績(jī)檔並計(jì)算平均第26?38行以try區(qū)塊來進(jìn)行讀取物件資料的動(dòng)作,第28行以O(shè)bjectInputStream的readObject()方法從串流讀取物件,成功讀出物件後,即在29行將counter變數(shù)加1。第35?37行將讀到的學(xué)生成績(jī)輸出到螢?zāi)簧?其中使用Student類別中所定義的getXXX()方法來取得各科的分?jǐn)?shù)及平均分?jǐn)?shù)。150讀取學(xué)生成績(jī)檔並計(jì)算平均第26?38行以try區(qū)塊讀取學(xué)生成績(jī)檔並計(jì)算平均第40?48行是catch檔案結(jié)束例外物件的區(qū)塊,當(dāng)readObject()方法讀到檔案結(jié)尾時(shí),即在螢?zāi)簧陷敵鲎x到的總筆數(shù)、各科的總平均,同時(shí)關(guān)閉ObjectInputStream串流物件。151讀取學(xué)生成績(jī)檔並計(jì)算平均第40?48行是catch檔讀取學(xué)生成績(jī)檔並計(jì)算平均由於前一個(gè)WriteObject.java程式設(shè)計(jì)成可讓使用者自由輸入不定數(shù)量的學(xué)生資料,所以這個(gè)ReadObject.java程式是用迴圈的方式持續(xù)讀Student物件,直到檔案結(jié)束,因此需以try/catch來處理EOFException例外物件,讓程式不會(huì)意外終止執(zhí)行。152讀取學(xué)生成績(jī)檔並計(jì)算平均由於前一個(gè)WriteObject.第16章資料輸入與輸出153第16章資料輸入與輸出1本章提要16-1甚麼是串流?16-2Java串流類別架構(gòu)16-3輸出、輸入資料16-4物件的讀寫16-5綜合演練154本章提要16-1甚麼是串流?2前言在本章之前,我們已多次用"importjava.io.*"敘述匯入Java的I/O(資料輸入與輸出)套件,並使用其中的BufferedReader類別的readLine()方法從鍵盤讀取使用者輸入的資料,以及用System.out.println()方法在螢?zāi)簧巷@示訊息或輸出程式執(zhí)行的結(jié)果。155前言在本章之前,我們已多次用"importjava.i前言但java.io套件的功能可不僅止於此,舉凡從電腦的螢?zāi)弧㈡I盤等各種裝置輸出或輸入資料,或是讀寫電腦中的文字檔、二元檔(binaryfile),甚至是讀寫ZIP格式的壓縮檔,都可透過java.io套件中的類別來完成。本章就要來介紹Java的資料輸入與輸出架構(gòu),以及如何使用java.io套件的各項(xiàng)I/O類別。156前言但java.io套件的功能可不僅止於此,舉凡從電腦16-1甚麼是串流?為了簡(jiǎn)化程式設(shè)計(jì)人員處理I/O的動(dòng)作,不管讀取資料或?qū)懭胭Y料的來源/目的為何(檔案、網(wǎng)路、或記憶體等等),都是以串流(stream)的方式進(jìn)行資料的讀取與寫入。而串流就是形容資料像一條河流一樣,將資料依序從資料來源中流出,或是流入目的地中。15716-1甚麼是串流?為了簡(jiǎn)化程式設(shè)計(jì)人員處理I/O的動(dòng)甚麼是串流?158甚麼是串流?6甚麼是串流?在java.io套件中,所有的資料輸出入類別都是以串流的方式來操作資料,不管讀取或?qū)懭?都離不開以下三個(gè)基本動(dòng)作:開啟串流(建構(gòu)串流物件)從串流讀取資料、或?qū)①Y料寫入串流關(guān)閉串流159甚麼是串流?在java.io套件中,所有的資料輸出入類甚麼是串流?從程式的觀點(diǎn),可供程式讀取的資料來源稱為輸入串流(inputstream);而可用來寫入資料的則稱為輸出串流(outputstream)。不管我們是從磁碟(檔案)、網(wǎng)路(URL)或其它來源或目的建立串流物件,讀寫的方式都相似,Java已替我們將其間的不同隱藏起來,讓我們可以用一致的方式來操作串流,大幅簡(jiǎn)化學(xué)習(xí)過程。160甚麼是串流?從程式的觀點(diǎn),可供程式讀取的資料來源稱為輸入串16-2Java串流類別架構(gòu)在java.io套件中,共有4組串流類別,這4組類別可分為兩大類:以byte為處理單位的輸出入串流,又可稱之為位元串流(ByteStreams)以char為處理單位的輸出入串流,又可稱之為字元串流(CharacterStreams)16116-2Java串流類別架構(gòu)在java.io套件中,位元串流位元串流是以8位元的byte為單位進(jìn)行資料的讀寫,位元串流有兩個(gè)最上層的抽像類別:InputStream(輸入)及OutputStream(輸出)。所有的輸出入位元串流都是由這兩個(gè)類別衍生出來的,例如我們已用過很多次的System.out,它是個(gè)java.io.PrintStream類別的物件,此類別是FilterOutputStream的子類別,而FilterOutputStream則是OutputStream的子類別。162位元串流位元串流是以8位元的byte為單位進(jìn)行資料的位元串流關(guān)於位元串流的主要類別,請(qǐng)參見以下的類別圖:163位元串流關(guān)於位元串流的主要類別,請(qǐng)參見以下的類別圖:11位元串流164位元串流12位元串流每種類別都適合於某類的讀取或?qū)懭氲膭?dòng)作,例如ByteArrayInputStream適用於讀取位元陣列;FileOutputStream則適用於寫入檔案。另外比較特別的是ObjectIntputStream和ObjectOutputStream,這兩個(gè)串流類別是特別為了讀寫我們自訂類別的物件而設(shè)計(jì),其詳細(xì)用法會(huì)在16-4節(jié)中介紹。165位元串流每種類別都適合於某類的讀取或?qū)懭氲膭?dòng)作,例如By位元串流這些串流類別的讀/寫方法都有個(gè)共通的特性,就是它們的原型宣告都註明"throwsIOException",所以使用這些方法時(shí),要記得用try/catch來執(zhí)行,或是在您的方法宣告也加上"throwsIOException"的註記,將例外拋給上層。166位元串流這些串流類別的讀/寫方法都有個(gè)共通的特性,就是它字元串流字元串流是以16位元的char為單位進(jìn)行資料的讀寫,字元串流同樣有兩個(gè)最上層的抽像類別Reader、Writer,分別對(duì)應(yīng)於位元串流的InputStream、OutputStream。這類串流類別主要是因應(yīng)國際化的趨勢(shì),為方便處理16位元的Unicode字元而設(shè)的,而且字元串流也會(huì)自動(dòng)分辦資料中的8位元ASCII字元和Unicode字元,不會(huì)將兩種資料弄混。167字元串流字元串流是以16位元的char為單位進(jìn)行資料字元串流字元串流類別的架構(gòu)和位元串流有些類似,而且其中各類別、方法的用法也都和位元串流中對(duì)應(yīng)的類別、方法相似,所以學(xué)會(huì)一種用法就等於學(xué)會(huì)兩種。不過Reader、Writer的衍生類別數(shù)量較少:168字元串流字元串流類別的架構(gòu)和位元串流有些類似,而且其中各類字元串流169字元串流17字元串流170字元串流1816-3輸出、輸入資料標(biāo)準(zhǔn)輸出、輸入檔案輸出、輸入讀寫二元檔17116-3輸出、輸入資料標(biāo)準(zhǔn)輸出、輸入19標(biāo)準(zhǔn)輸出、輸入所謂標(biāo)準(zhǔn)輸出一般就是指螢?zāi)?而標(biāo)準(zhǔn)輸入則是指鍵盤,在前幾章的程式中,就是從鍵盤取得使用者輸入的資料,從螢?zāi)惠敵鲇嵪⒓皥?zhí)行結(jié)果。172標(biāo)準(zhǔn)輸出、輸入所謂標(biāo)準(zhǔn)輸出一般就是指螢?zāi)?而標(biāo)準(zhǔn)輸入則是指標(biāo)準(zhǔn)輸出在System類別中,有兩個(gè)PrintStream類別的成員:out成員:代表標(biāo)準(zhǔn)輸出裝置,一般而言,都是指電腦螢?zāi)?。不過我們可以利用轉(zhuǎn)向的方式,讓輸出的內(nèi)容是輸出到檔案、印表機(jī)、或遠(yuǎn)端的終端機(jī)等等。例如在命令提示字元視窗中,可以用“dir>test”的方式,使dir原本會(huì)顯示在螢?zāi)簧系馁Y訊『轉(zhuǎn)向』存到"test"這個(gè)檔案中。(在Unix/Linux系統(tǒng)下也可用相同的轉(zhuǎn)向技巧,例如"ls>test")。173標(biāo)準(zhǔn)輸出在System類別中,有兩個(gè)Prin標(biāo)準(zhǔn)輸出err成員:代表標(biāo)準(zhǔn)『示誤訊息』輸出裝置,同樣預(yù)設(shè)為螢?zāi)?。以往?dāng)應(yīng)用程式執(zhí)行過程中遇到錯(cuò)誤並需顯示相關(guān)訊息通知使用者,此時(shí)就是將訊息輸出到此裝置。雖然err與out同樣預(yù)設(shè)為螢?zāi)?但我們將out轉(zhuǎn)向時(shí),err並不會(huì)跟著轉(zhuǎn)向。174標(biāo)準(zhǔn)輸出err成員:代表標(biāo)準(zhǔn)『示誤訊息』輸出裝置,同樣預(yù)標(biāo)準(zhǔn)輸出舉例來說,如果執(zhí)行"dirABC>test"這個(gè)命令,但資料夾中並無ABC這個(gè)檔案,此時(shí)dir指令仍會(huì)將"找不到檔案"的示誤訊息顯示在螢?zāi)簧?而不會(huì)存到test檔案中。175標(biāo)準(zhǔn)輸出舉例來說,如果執(zhí)行"dirABC>test標(biāo)準(zhǔn)輸出PrintStream類別多重定義了適用於Java各種資料型別的print()、println()方法(後者會(huì)在輸出資料後再多輸出一個(gè)換行字元以進(jìn)行換行),所以我們能用這兩個(gè)方法輸出任何資料型別,Java都會(huì)自動(dòng)以適當(dāng)?shù)母袷捷敵觥?76標(biāo)準(zhǔn)輸出PrintStream類別多重定義了適用於Jav標(biāo)準(zhǔn)輸出此外,PrintStream類別還有一對(duì)多重定義的write()方法,其功能也是輸出位元資料,但此時(shí)參數(shù)是資料的『位元值』。例如我們要輸出"A"這個(gè)字元,必須指定其ASCII碼65,例如"write(65);"。另一個(gè)write()方法則是可輸出位元陣列的元素,且可指定要從第幾個(gè)元素開始輸出、共輸出幾個(gè)元素:177標(biāo)準(zhǔn)輸出此外,PrintStream類別還有一對(duì)多重定義標(biāo)準(zhǔn)輸出PrintStream類別有個(gè)和其它串流類別不同的特點(diǎn),就是它的方法都不會(huì)拋出IOException例外。以下這個(gè)簡(jiǎn)單的程式示範(fàn)了這幾個(gè)方法的用法及效果:178標(biāo)準(zhǔn)輸出26標(biāo)準(zhǔn)輸出179標(biāo)準(zhǔn)輸出27標(biāo)準(zhǔn)輸出180標(biāo)準(zhǔn)輸出28標(biāo)準(zhǔn)輸出第8?12行的迴圈會(huì)分別用print()和write()方法輸出a[]陣列中的元素。print()方法會(huì)將各元素當(dāng)整數(shù)值輸出,所以可正??吹捷敵鲋?;使用write()方法時(shí)則是將元素值當(dāng)成一個(gè)2進(jìn)元數(shù)值輸出,對(duì)螢?zāi)欢?就是將元素值當(dāng)成ASCII碼,然後輸出對(duì)應(yīng)的ASCII字元。181標(biāo)準(zhǔn)輸出第8?12行的迴圈會(huì)分別用print()和標(biāo)準(zhǔn)輸出以a[0]為例,ASCII碼10是換行字元,所以輸出這行後會(huì)自動(dòng)換行;至於ASCII碼20對(duì)應(yīng)的字元?jiǎng)t是一個(gè)特殊的控制字元,所以a[1]這行後面看不到內(nèi)容;至於最後一個(gè)a[4]:160對(duì)應(yīng)的字碼超出127(該字元是a上面多一撇),所以在中文環(huán)境被當(dāng)成Big-5字碼第一碼,但因?yàn)闊o第二碼,因此只輸出一個(gè)問號(hào)。182標(biāo)準(zhǔn)輸出以a[0]為例,ASCII碼10是換行字標(biāo)準(zhǔn)輸出第14行改用err物件以write()方法輸出b陣列的全部?jī)?nèi)容。由於ASCII碼7是個(gè)特殊的BEL字元,它只會(huì)讓電腦發(fā)出嗶聲,但不會(huì)輸出任何『字』,而ASCII碼32對(duì)應(yīng)的是『空白』字元,所以這行敘述只會(huì)讓電腦發(fā)出三聲嗶聲,但螢?zāi)簧峡床坏饺魏屋敵觥?83標(biāo)準(zhǔn)輸出第14行改用err物件以write()方標(biāo)準(zhǔn)輸出若要測(cè)試System.out、System.err的差異,可改以轉(zhuǎn)向的方式來執(zhí)行這個(gè)範(fàn)例程式,例如:184標(biāo)準(zhǔn)輸出若要測(cè)試System.out、System.err標(biāo)準(zhǔn)輸入標(biāo)準(zhǔn)輸入一般指的是鍵盤,但同樣可以利用轉(zhuǎn)向的方式從其它裝置來取得。不過細(xì)心的讀者或許發(fā)現(xiàn),前幾章的範(fàn)例程式並未直接用System.in這個(gè)物件來讀取鍵盤輸入,我們都是另外建立一個(gè)BufferedReader類別的物件,然後用這個(gè)物件來讀取鍵盤輸入。為什麼要這樣做呢?原因很簡(jiǎn)單:就是為了方便處理。185標(biāo)準(zhǔn)輸入標(biāo)準(zhǔn)輸入一般指的是鍵盤,但同樣可以利用轉(zhuǎn)向的方式從標(biāo)準(zhǔn)輸入System.in這個(gè)成員是InputStream類別的物件,換言之它是將標(biāo)準(zhǔn)輸入當(dāng)成位元串流來處理,所以我們?nèi)粲盟鼇碜x取鍵盤輸入,讀到的都是位元的形式,處理上並不方便(想一下如果要讀取中文或Unicode字元,就需進(jìn)行額外的處理)。此外直接讀取鍵盤輸入串流時(shí),由於電腦鍵盤緩衝區(qū)的運(yùn)作方式,會(huì)造成一些不易處理的狀況。186標(biāo)準(zhǔn)輸入System.in這個(gè)成員是InputStrea標(biāo)準(zhǔn)輸入為讓讀者瞭解直接使用System.in的情況,我們先介紹InputStream類別的read()方法:187標(biāo)準(zhǔn)輸入為讓讀者瞭解直接使用System.in的情況,標(biāo)準(zhǔn)輸入使用這些方法時(shí),都需處理IOException例外,或是單純拋給上層處理。我們就來看一下透過System.in物件用這些方法直接讀取鍵盤輸入的情形:188標(biāo)準(zhǔn)輸入使用這些方法時(shí),都需處理IOException標(biāo)準(zhǔn)輸入189標(biāo)準(zhǔn)輸入37標(biāo)準(zhǔn)輸入190標(biāo)準(zhǔn)輸入38標(biāo)準(zhǔn)輸入191標(biāo)準(zhǔn)輸入39標(biāo)準(zhǔn)輸入第9、18行分別用不同的read()方法讀取鍵盤輸入的位元資料。第10行叫用Character.toString()方法(參見第17章)將字元轉(zhuǎn)成字串。第13、22行用Math.pow()方法(參見第17章)計(jì)算2的N次方。192標(biāo)準(zhǔn)輸入第9、18行分別用不同的read()方法讀取標(biāo)準(zhǔn)輸入讀者可能會(huì)覺得很奇怪,為何會(huì)有如上的執(zhí)行結(jié)果?最主要的原因是範(fàn)例程式第1次呼叫read()方法只讀取1個(gè)位元,但使用者可能輸入2位數(shù)字(多個(gè)位元)、且InputStream的read()方法也會(huì)讀到[Enter]按鍵的資訊所造成的。193標(biāo)準(zhǔn)輸入讀者可能會(huì)覺得很奇怪,為何會(huì)有如上的執(zhí)行結(jié)果?最主標(biāo)準(zhǔn)輸入回頭看第一個(gè)執(zhí)行結(jié)果:程式第1次要求輸入,我們輸入2時(shí),read()方法傳回的是“2”這個(gè)字元的ASCII碼,也就是50,所以程式必須進(jìn)行一些轉(zhuǎn)換,才能得到整數(shù)以進(jìn)行運(yùn)算。程式第2次要求輸入時(shí),我們還未輸入,程式就直接顯示例外訊息而結(jié)束,這是因?yàn)榍耙淮屋斎?時(shí)按下的[Enter]鍵會(huì)產(chǎn)生歸位(CarriageReturn)及換行(LineFeed)字元(控制碼分別13及10)。194標(biāo)準(zhǔn)輸入回頭看第一個(gè)執(zhí)行結(jié)果:程式第1次要求輸入,我們標(biāo)準(zhǔn)輸入所以第2次讀取時(shí),read()方法便直接讀到這些字元,造成輸入的字串變成空字串,導(dǎo)致第19行程式進(jìn)行轉(zhuǎn)換時(shí)發(fā)生例外。至於第2個(gè)執(zhí)行結(jié)果,則是在第1次輸入時(shí),就故意輸入多個(gè)字元。結(jié)果第2次的read()方法就讀到前次未讀到的'5',所以就直接計(jì)算2的5次方。195標(biāo)準(zhǔn)輸入所以第2次讀取時(shí),read()方法便直接讀到標(biāo)準(zhǔn)輸入雖然[Enter]鍵的問題並非不能解決,但一來這樣做會(huì)讓程式多做額外的處理,二來大多數(shù)的應(yīng)用程式都是要求使用者輸入『字元』而非位元,所以我們會(huì)用字元串流來包裝System.in,達(dá)到簡(jiǎn)化處理的目的。196標(biāo)準(zhǔn)輸入雖然[Enter]鍵的問題並非不能解決,但一來用字元串流來包裝System.in為了方便我們從鍵盤取得資料,我們會(huì)以字元串流來包裝System.in這個(gè)位元串流,『包裝』(wrap)意指用System.in來建立字元串流的物件,所以對(duì)程式來說,它使用的是『字元』串流,而非原始的System.in『位元』串流。197用字元串流來包裝System.in為了方便我們從鍵盤取得資用字元串流來包裝System.in以前幾章取得鍵盤輸入的方式為例,我們都使用如下的程式:198用字元串流來包裝System.in以前幾章取得鍵盤輸入的方用字元串流來包裝System.in上述程式就是先將System.in物件先包裝成InputStreamReader物件,然後再包一層變成BufferedReader物件,最後才用此物件的readLine()方法來取得輸入。之所以要包兩層,主要原因可分為2點(diǎn):199用字元串流來包裝System.in上述程式就是先將Sys用字元串流來包裝System.inInputStreamReader是個(gè)特殊的字元串流,它的功用就是從位元串流取得輸入,然後將這些位元解讀成字元。因此在建構(gòu)InputStreamReader物件時(shí),必須以一個(gè)位元串流物件為參數(shù)來呼叫其建構(gòu)方法。但I(xiàn)nputStreamReader在使用上仍有前述[Enter]鍵的問題,操作並不方便。因此一般都會(huì)將它再包裝成其它更方便使用的串流類別物件,例如BufferedReader。200用字元串流來包裝System.inInputStreamR用字元串流來包裝System.inBufferedReader是所謂的緩衝式輸入串流,也就是先將串流的輸入存到一記憶體緩衝區(qū)中,程式再到這個(gè)緩衝區(qū)讀取輸入。在讀取檔案時(shí)這種緩衝式輸入效率較佳,而讀取鍵盤輸入時(shí),也可免去處理[Enter]鍵的問題。201用字元串流來包裝System.inBufferedRead用字元串流來包裝System.in但BufferedReader只有以Reader物件為參數(shù)的建構(gòu)方法,因此我們必須先將System.in轉(zhuǎn)成InputStreamReader物件,才能用後者呼叫BufferedReader的建構(gòu)方法,產(chǎn)生所要的物件。使用BufferedReader的readLine()方法讀取輸入時(shí),每次會(huì)讀取『一行』的內(nèi)容,且會(huì)自動(dòng)忽略該行結(jié)尾的歸位及換行字元,因此可順利解決[Enter]鍵的問題。請(qǐng)參考以下範(fàn)例:202用字元串流來包裝System.in但BufferedRe用字元串流來包裝System.in203用字元串流來包裝System.in51用字元串流來包裝System.in204用字元串流來包裝System.in52用字元串流來包裝System.in205用字元串流來包裝System.in53用字元串流來包裝System.in第09行用InputStreamReader包裝System.in。第14行以while迴圈的方式連續(xù)讀取多個(gè)字元,遇到換行字元(字碼為10)時(shí)即停止。第18、19行改以for迴圈輸出所有讀到的字元。第24行使用BufferedReader包裝第09行建立的InputStreamReader物件。
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度新能源研發(fā)與市場(chǎng)推廣合作合同2篇
- 銅川2024年陜西銅川市教育類高層次人才校園招聘15人筆試歷年典型考點(diǎn)(頻考版試卷)附帶答案詳解
- 娛樂產(chǎn)業(yè)營銷渠道創(chuàng)新-洞察分析
- 網(wǎng)絡(luò)社交文化變遷-洞察分析
- 2023-2024年企業(yè)主要負(fù)責(zé)人安全培訓(xùn)考試題參考答案
- 2023年-2024年新員工入職前安全教育培訓(xùn)試題含完整答案(各地真題)
- 行程編碼隱私保護(hù)-洞察分析
- 文化外交與青年交流策略-洞察分析
- 主要施工機(jī)械設(shè)備計(jì)劃
- 運(yùn)輸安全標(biāo)準(zhǔn)化自評(píng)管理制度
- JTGT F20-2015 公路路面基層施工技術(shù)細(xì)則
- 機(jī)械加工廠計(jì)劃管理
- 《美術(shù)策展方案》課件
- 幼兒教師專業(yè)發(fā)展及《幼兒園教師專業(yè)標(biāo)準(zhǔn)》解讀課件
- 云南保山電力股份有限公司招聘筆試題庫
- 銀行業(yè)聲譽(yù)風(fēng)險(xiǎn)管理培訓(xùn)
- 醫(yī)院季度投訴分析整改報(bào)告
- 2023-2024學(xué)年江西省吉安市吉州區(qū)八年級(jí)(上)期末數(shù)學(xué)試卷(含解析)
- 全面醫(yī)療安全生產(chǎn)隱患排查清單
- 對(duì)吸毒人員管控措施
- 煤礦四新安全教育課件
評(píng)論
0/150
提交評(píng)論