歡迎來到裝配圖網(wǎng)! | 幫助中心 裝配圖網(wǎng)zhuangpeitu.com!
裝配圖網(wǎng)
ImageVerifierCode 換一換
首頁 裝配圖網(wǎng) > 資源分類 > DOC文檔下載  

[Java就業(yè)培訓(xùn)教程] 第七章 IO

  • 資源ID:59489280       資源大?。?span id="yugrshw" class="font-tahoma">4.60MB        全文頁數(shù):42頁
  • 資源格式: DOC        下載積分:16積分
快捷下載 游客一鍵下載
會員登錄下載
微信登錄下載
三方登錄下載: 微信開放平臺登錄 支付寶登錄   QQ登錄   微博登錄  
二維碼
微信掃一掃登錄
下載資源需要16積分
郵箱/手機:
溫馨提示:
用戶名和密碼都是您填寫的郵箱或者手機號,方便查詢和重復(fù)下載(系統(tǒng)自動生成)
支付方式: 支付寶    微信支付   
驗證碼:   換一換

 
賬號:
密碼:
驗證碼:   換一換
  忘記密碼?
    
友情提示
2、PDF文件下載后,可能會被瀏覽器默認(rèn)打開,此種情況可以點擊瀏覽器菜單,保存網(wǎng)頁到桌面,就可以正常下載了。
3、本站不支持迅雷下載,請使用電腦自帶的IE瀏覽器,或者360瀏覽器、谷歌瀏覽器下載即可。
4、本站資源下載后的文檔和圖紙-無水印,預(yù)覽文檔經(jīng)過壓縮,下載后原文更清晰。
5、試題試卷類文檔,如果標(biāo)題沒有明確說明有答案則都視為沒有答案,請知曉。

[Java就業(yè)培訓(xùn)教程] 第七章 IO

第7章 IO/輸入輸出大多數(shù)應(yīng)用程序都需要與外部設(shè)備進行數(shù)據(jù)交換,最常見的外部設(shè)備包含磁盤和網(wǎng)絡(luò),IO就是指應(yīng)用程序?qū)@些設(shè)備的數(shù)據(jù)輸入與輸出,在程序中,鍵盤被當(dāng)作輸入文件,顯示器被當(dāng)作輸出文件使用。Java語言定義了許多類專門負(fù)責(zé)各種方式的輸入輸出,這些類都被放在java.io包中。7.1 File類File類是IO包中唯一代表磁盤文件本身的對象,F(xiàn)ile類定義了一些與平臺無關(guān)的方法來操縱文件,通過調(diào)用File類提供的各種方法,我們能夠創(chuàng)建、刪除文件,重命名文件,判斷文件的讀寫權(quán)限及是否存在,設(shè)置和查詢文件的最近修改時間。在Java中,目錄也被當(dāng)作File使用,只是多了一些目錄特有的功能可以用list方法列出目錄中的文件名。在Unix下的路徑分隔符為(/),在Dos下的路徑分隔符為(),Java可以正確處理Unix和Dos的路徑分隔符,即使我們在Windows環(huán)境下使用(/)作為路徑分隔符,Java仍然能夠正確處理。我們用下面的一個簡單應(yīng)用來演示一下File類用法,判斷某個文件是否存在,存在則刪除,不存在則創(chuàng)建,讀者可以在Windows的資源管理器下觀察到這個變化。程序清單:FileTest.javaimport java.io.*;public class FileTestpublic static void main(String args)File f=new File("c:1.txt");if(f.exists()f.delete();elsetryf.createNewFile();catch(Exception e)System.out.println(e.getMessage();System.out.println("File name:"+f.getName();System.out.println("File path:"+f.getPath();System.out.println("Abs path:"+f.getAbsolutePath();System.out.println("Parent:"+f.getParent();System.out.println(f.exists()?"exists":"does not exist");System.out.println(f.canWrite()?"is writeable":"is not writeable");System.out.println(f.canRead()?"is readable":"is not readable");System.out.println(f.isDirectory()?"is ":"is not"+" a directory");System.out.println(f.isFile()?"is normal file":"might be a named pipe");System.out.println(f.isAbsolute()?"is absolute":"is not absolute");System.out.println("File last modified:"+f.lastModified();System.out.println("File size:"+f.length()+" Bytes");當(dāng)運行這個程序時會因為文件1.txt的存在和不存在而出現(xiàn)兩種結(jié)果:結(jié)果1:File name:1.txtFile path:c:1.txtAbs path:c:1.txtParent:c:existsis writeableis readableis not a directoryis normal fileis absoluteFile last modified:1051755103126File size:0 Bytes結(jié)果2:File name:1.txtFile path:c:1.txtAbs path:c:1.txtParent:c:does not existis not writeableis not readableis not a directorymight be a named pipeis absoluteFile last modified:0File size:0 Bytes注:delete方法刪除由File對象的路徑所表示的磁盤文件。它只能刪除普通文件,而不能刪除目錄,即使是空目錄也不行。關(guān)于File類的其它方法,是沒法死記硬背的,讀者在需要時自己查看JDK文檔,應(yīng)該能夠明白怎么使用。初步接觸了File類,我們發(fā)現(xiàn)File類不能訪問文件的內(nèi)容,即不能夠從文件中讀取數(shù)據(jù)或往文件里寫數(shù)據(jù),它只能對文件本身的屬性進行操作。7.2 RandomAccessFile類RandomAccessFile類可以說是Java語言中功能最為豐富的文件訪問類,它提供了眾多的文件訪問方法。RandomAccessFile類支持“隨機訪問”方式,我們可以跳轉(zhuǎn)到文件的任意位置處讀寫數(shù)據(jù)。在你訪問一個文件的時候,不想把文件從頭讀到尾,并希望像訪問一個數(shù)據(jù)庫一樣的訪問一個文本文件,使用RandomAccessFile類就是你的最佳選擇。RandomAccessFile對象類有個位置指示器,指向當(dāng)前讀寫處的位置,當(dāng)讀寫n 個字節(jié)后,文件指示器將指向這n個字節(jié)后的下一個字節(jié)處。剛打開文件時,文件指示器指向文件的開頭處,我們可以移動文件指示器到新的位置,隨后的讀寫操作將從新的位置開始。RandomAccessFile在等長記錄格式文件的隨機(相對順序而言)讀取時有很大的優(yōu)勢,但該類僅限于操作文件,不能訪問其他的IO設(shè)備,如網(wǎng)絡(luò),內(nèi)存映象等。有關(guān)RandomAccessFile類中的成員方法及使用說明,請參閱JDK文檔。下面是一個使用RandomAccessFile的例子,往文件中寫入三名員工的信息,然后按照第二名員工,第一名員工,第三名員工的先后順序讀出。RandomAccessFile可以以只讀或讀寫方式打開文件,具體使用哪種方式取決于我們創(chuàng)建RandomAccessFile類對象的構(gòu)造方式:new RandomAccessFile(f,"rw"); /讀寫方式new RandomAccessFile(f,"r"); /只讀方式注:當(dāng)我們的程序需要以讀寫的方式打開一個文件時,如果這個文件不存在,程序會為你創(chuàng)建它。我們還需要設(shè)計一個類來封裝員工信息。一個員工信息就是文件中的一條記錄,我們必須保證每條記錄在文件中的大小相同,也就是每個員工的姓名字段在文件中的長度是一樣的,我們才能夠準(zhǔn)確定位每條記錄在文件中的具體位置。假設(shè)name中有八個字符,少于八個則補空格(這里我們用"u0000"),多于八個則去掉后面多余的部分。由于年齡是整型數(shù),不管這個數(shù)有多大,只要它不超過整型數(shù)的范圍,在內(nèi)存中都是占4個字節(jié)大小。程序清單:RandomFileTest.javaimport java.io.*;public class RandomFileTestpublic static void main(String args) throws Exception Employee e1 = new Employee("zhangsan",23);Employee e2 = new Employee("Lisi",24);Employee e3 = new Employee("Wangwu",25);RandomAccessFile ra=new RandomAccessFile("c:1.txt","rw");ra.write(e1.name.getBytes();ra.writeInt(e1.age);ra.write(e2.name.getBytes();ra.writeInt(e2.age);ra.write(e3.name.getBytes();ra.writeInt(e3.age);ra.close();RandomAccessFile raf=new RandomAccessFile("c:1.txt","r");int len=8;raf.skipBytes(12); /跳過第一個員工的信息,其中姓名8字節(jié),年齡4字節(jié)System.out.println("第二個員工信息:");String str=""for(int i=0;i<len;i+)str=str+(char)raf.readByte();System.out.println("name:"+str);System.out.println("age:"+raf.readInt();System.out.println("第一個員工的信息:");raf.seek(0); /將文件指針移動到文件開始位置str=""for(int i=0;i<len;i+)str=str+(char)raf.readByte();System.out.println("name:"+str);System.out.println("age:"+raf.readInt();System.out.println("第三個員工的信息:");raf.skipBytes(12); /跳過第二個員工信息str=""for(int i=0;i<len;i+)str=str+(char)raf.readByte();System.out.println("name:"+str.trim();System.out.println("age:"+raf.readInt();raf.close();class EmployeeString name;int age;final static int LEN=8;public Employee(String name,int age)if(name.length()>LEN)name = name.substring(0,8);elsewhile(name.length()<LEN)name=name+"u0000"this.name=name;this.age=age;運行結(jié)果:第二個員工信息:name:Lisiage:24第一個員工的信息:name:zhangsanage:23第三個員工的信息:name:Wangwuage:25c盤還多了個文件1.txt:圖7.1上面的這個程序完成了我們想要的功能,演示了RandomAccessFile類的作用。String.substring(int beginIndex,int endIndex)方法可以用于取出一個字符串中的部分子字符串,要注意的一個細(xì)節(jié)是:子字符串中的第一個字符對應(yīng)的是原字符串中的腳標(biāo)為beginIndex處的字符,但最后的字符對應(yīng)的是原字符串中的腳標(biāo)為endIndex-1處的字符,而不是endIndex處的字符。在實際生活中,我們常用的數(shù)據(jù)庫和數(shù)據(jù)庫管理工具實際上就是這種原理。我們的1.txt就相當(dāng)于數(shù)據(jù)庫的數(shù)據(jù)文件,而我們這個程序提供了往這個數(shù)據(jù)文件寫入和讀取數(shù)據(jù)的功能。7.3 節(jié)點流7.3.1 理解流的概念數(shù)據(jù)流是一串連續(xù)不斷的數(shù)據(jù)的集合,就象水管里的水流,在水管的一端一點一點地供水,而在水管的另一端看到的是一股連續(xù)不斷的水流。數(shù)據(jù)寫入程序可以是一段、一段地向數(shù)據(jù)流管道中寫入數(shù)據(jù),這些數(shù)據(jù)段會按先后順序形成一個長的數(shù)據(jù)流。對數(shù)據(jù)讀取程序來說,看不到數(shù)據(jù)流在寫入時的分段情況,每次可以讀取其中的任意長度的數(shù)據(jù),但只能先讀取前面的數(shù)據(jù)后,再讀取后面的數(shù)據(jù)。不管寫入時是將數(shù)據(jù)分多次寫入,還是作為一個整體一次寫入,讀取時的效果都是完全一樣的。我們將IO流類分為兩個大類,節(jié)點流類和過濾流類(也叫處理流類)。程序用于直接操作目標(biāo)設(shè)備所對應(yīng)的類叫節(jié)點流類,程序也可以通過一個間接流類去調(diào)用節(jié)點流類,以達到更加靈活方便地讀寫各種類型的數(shù)據(jù),這個間接流類就是過濾流類(也叫處理流類),我更喜歡稱之為包裝類。不管叫什么,都只是一個代名詞而已,讀者不要太在意,你可以根據(jù)自己的習(xí)慣和喜好來定。7.3.2 InputStream與OutputStream程序可以從中連續(xù)讀取字節(jié)的對象叫輸入流,用InputStream類完成,程序能向其中連續(xù)寫入字節(jié)的對象叫輸出流,用OutputStream類完成。InputStream與OutputStream對象是兩個抽象類,還不能表明具體對應(yīng)哪種IO設(shè)備。它們下面有許多子類,包括網(wǎng)絡(luò),管道,內(nèi)存,文件等具體的IO設(shè)備,如FileInputStream類對應(yīng)的就是文件輸入流,是一個節(jié)點流類,我們將這些節(jié)點流類所對應(yīng)的IO源和目標(biāo)稱為流節(jié)點(Node)。F指點迷津:很多人搞不清程序要將A文件的內(nèi)容寫入B文件中,程序?qū)文件的操作所用的是輸出類還是輸入類這個問題。讀者也先自己想想,再記住下面的話,輸入輸出類是相對程序而言的,而不是代表文件的,所以我們應(yīng)該創(chuàng)建一個輸入類來完成對A文件的操作,創(chuàng)建一個輸出類來完成對B文件的操作。InputStream定義了Java的輸入流模型。該類中的所有方法在遇到錯誤的時候都會引發(fā)IOException異常,下面是InputStream類中方法的一個簡要說明:ü int read()返回下一個輸入字節(jié)的整型表示,,如果返回-1表示遇到流的末尾,結(jié)束。ü int read(byte b)讀入b.length個字節(jié)放到b中并返回實際讀入的字節(jié)數(shù)。ü int read(byte b,int off,int len) 這個方法表示把流中的數(shù)據(jù)讀到,數(shù)組b中,第off個開始的len個數(shù)組元素中.ü long skip(long n) 跳過輸入流上的n個字節(jié)并返回實際跳過的字節(jié)數(shù)。ü int availabale() 返回當(dāng)前輸入流中可讀的字節(jié)數(shù)。ü void mark(int readlimit)在輸入流的當(dāng)前位置處放上一個標(biāo)志,允許最多再讀入readlimit個字節(jié)。ü void reset() 把輸入指針返回到以前所做的標(biāo)志處。ü boolean markSupported() 如果當(dāng)前流支持mark/reset操作就返回true。ü void close() 在操作完一個流后要使用此方法將其關(guān)閉, 系統(tǒng)就會釋放與這個流相關(guān)的資源。InputStream是一個抽象類,程序中實際使用的是它的各種子類對象。不是所有的子類都會支持InputStream中定義的某些方法的,如skip,mark,reset等,這些方法只對某些子類有用。F指點迷津:一個對象在沒有引用變量指向它時會變成垃圾,最終會被垃圾回收器從內(nèi)存中清除。對于我們創(chuàng)建的流對象,干嘛還要“調(diào)用close方法將它關(guān)閉,以釋放與其相關(guān)的資源”呢?這相關(guān)的資源到底是些什么呢?我們在程序中創(chuàng)建的對象都是對應(yīng)現(xiàn)實世界中有形或無形的事物,計算機操作系統(tǒng)所產(chǎn)生的東西當(dāng)然也是現(xiàn)實世界中的事物,也就是說,程序中的對象也可以對應(yīng)計算機操作系統(tǒng)所產(chǎn)生的一個其他東西,專業(yè)地說,這些東西叫資源,流就是操作系統(tǒng)產(chǎn)生的一種資源。當(dāng)我們在程序中創(chuàng)建了一個IO流對象,同時系統(tǒng)內(nèi)也會創(chuàng)建了一個叫流的東西,在這種情況下,計算機內(nèi)存中實際上產(chǎn)生了兩個事物,一個是Java程序中的類的實例對象,一個是系統(tǒng)本身產(chǎn)生的某種資源,我們以后講到的窗口,Socket等都是這樣的情況。Java垃圾回收器只能管理程序中的類的實例對象,沒法去管理系統(tǒng)產(chǎn)生的資源,所以程序需要調(diào)用close方法,去通知系統(tǒng)釋放其自身產(chǎn)生的資源。OutputStream是一個定義了輸出流的抽象類,這個類中的所有方法均返回void,并在遇到錯誤時引發(fā)IOException異常。下面是OutputStream的方法:ü void write(int b) 將一個字節(jié)寫到輸出流。注意,這里的參數(shù)是int型,它允許write使用表達式而不用強制轉(zhuǎn)換成byte型。ü void write(byte b) 將整個字節(jié)數(shù)組寫到輸出流中。ü void write(byte b,int off,int len) 將字節(jié)數(shù)組b中的從off開始的len個字節(jié)寫到輸出流。ü void flush徹底完成輸出并清空緩沖區(qū)。ü void close關(guān)閉輸出流。&多學(xué)兩招:計算機訪問外部設(shè)備,要比直接訪問內(nèi)存慢得多,如果我們每一次write方法的調(diào)用都直接寫到外部設(shè)備(如直接寫入硬盤文件),CPU就要花費更多的時間等待外部設(shè)備;如果我們開辟一個內(nèi)存緩沖區(qū),程序的每一次write方法都是寫到這個內(nèi)存緩沖區(qū)中,只有這個緩沖區(qū)被裝滿后,系統(tǒng)才將這個緩沖區(qū)的內(nèi)容一次集中寫到外部設(shè)備。使用內(nèi)存緩沖區(qū)有兩個方面的好處,一是有效地提高了CPU的使用率,二是write并沒有馬上真正寫入到外設(shè),我們還有機會回滾部分寫入的數(shù)據(jù)。使用緩沖區(qū),能提高整個計算機系統(tǒng)的效率,但也會降低單個程序自身的效率,由于有這么一個中間緩沖區(qū),數(shù)據(jù)并沒有馬上寫入到目標(biāo)中去,例如在網(wǎng)絡(luò)流中,就會造成一些滯后。對于輸入流,我們也可以使用緩沖區(qū)技術(shù)。在程序與外部設(shè)備之間到底用不用緩沖區(qū),是由編程語言本身決定的,我們通常用的C語言默認(rèn)情況下就會使用緩沖區(qū),而在Java語言中,有的類使用了緩沖區(qū),有的類沒有使用緩沖區(qū),我們還可以在程序中使用專門的包裝類來實現(xiàn)自己的緩沖區(qū)。flush方法就是用于即使在緩沖區(qū)沒有滿的情況下,也將緩沖區(qū)的內(nèi)容強制寫入到外設(shè),習(xí)慣上稱這個過程為刷新??梢姡琭lush方法不是對所有的OutputStream子類都起作用的,它只對那些使用緩沖區(qū)的OutputStream子類有效。如果我們調(diào)用了close方法,系統(tǒng)在關(guān)閉這個流之前,也會將緩沖區(qū)的內(nèi)容刷新到硬盤文件的。作者開發(fā)過一個郵件服務(wù)器程序,需要7*24小時不間斷工作,這個服務(wù)器程序要面對internet上各種可能的非法格式的數(shù)據(jù)輸入和攻擊,而我的程序正好又沒考慮到某種非法格式的數(shù)據(jù),一旦碰到這樣的情況,程序就會崩潰。有經(jīng)驗的人都知道,為了找出服務(wù)器程序崩潰的原因,我們可以將程序每次接收到的數(shù)據(jù)都記錄到一個文件中,當(dāng)服務(wù)器程序崩潰后,我們便打開這個記錄文件,查看最后記錄的那條數(shù)據(jù),這個數(shù)據(jù)就是讓我的程序斃命的罪魁禍?zhǔn)?,然后拿著這條數(shù)據(jù)一步步測試我們的程序,就很容易找出程序中的問題了。遺憾的是,我每次用最后記錄的這條數(shù)據(jù)測試我的程序,程序均安然無恙。最后,我發(fā)現(xiàn)就是因為有緩沖區(qū)的原因,緩沖區(qū)的內(nèi)容還沒來得及刷新到硬盤文件,程序就崩潰了,所以,文件中并沒有記錄最后接收到的那些數(shù)據(jù),我在文件中看到的最后以條記錄并不是真正最后接收到的那條數(shù)據(jù)。發(fā)現(xiàn)了這個原因,我修改程序,在每一次調(diào)用write語句后,都立即調(diào)用flush語句,這樣,我就終于找到了肇事元兇,并修復(fù)了程序的這個漏洞。盡管我以前從來沒有真正認(rèn)真思考和編程試驗過緩沖區(qū)問題,但是正因為還有那么一點點概念和印象,所以,在出現(xiàn)問題時,我才能從多方面去思考并最終解決問題。我建議讀者花更多的時間去開闊自己的知識面和思維,了解更多的原理,而不是去花大量時間去死記硬背某些細(xì)節(jié)和術(shù)語,特別是一個類中的每個函數(shù)名的具體拼寫、具體的參數(shù)形式,Java中有哪些關(guān)鍵字等這些死板的東西,只要有個印象就足夠了。7.3.3 FileInputStream與FileOutputStream這兩個流節(jié)點用來操作磁盤文件,在創(chuàng)建一個FileInputStream對象時通過構(gòu)造函數(shù)指定文件的路徑和名字,當(dāng)然這個文件應(yīng)當(dāng)是存在的和可讀的。在創(chuàng)建一個 FileOutputStream對象時指定文件如果存在將要被覆蓋。下面是對同一個磁盤文件創(chuàng)建FileInputStream對象的兩種方式。其中用到的兩個構(gòu)造函數(shù)都可以引發(fā)FileNotFoundException異常:FileInputStream inOne=new FileInputStream("hello.test");File f = new File("hello.test"); FileInputStream inTwo = new FileInputStream(f);盡管第一個構(gòu)造函數(shù)更簡單,但第二個構(gòu)造函數(shù)允許在把文件連接到輸入流之前對文件做進一步分析。FileOutputStream對象也有兩個和FileInputStream對象具有相同參數(shù)的構(gòu)造函數(shù),創(chuàng)建一個FileOutputStream對象時,可以為其指定還不存在的文件名,但不能是存在的目錄名,也不能是一個已被其他程序打開了的文件。FileOutputStream先創(chuàng)建輸出對象,然后再準(zhǔn)備輸出。其實在上一章中講Properties類的時候,我們已經(jīng)使用過這兩個類。在下面的例子中,我們用FileOutputStream類向文件中寫入一串字符,并用FileInputStream讀出。程序清單:FileStream.javaimport java.io.*;public class FileStreampublic static void main(String args)File f = new File("hello.txt"); tryFileOutputStream out = new FileOutputStream(f);byte buf="www.it315.org".getBytes();out.write(buf);out.close();catch(Exception e)System.out.println(e.getMessage();tryFileInputStream in = new FileInputStream(f);byte buf = new byte1024;int len = in.read(buf);System.out.println(new String(buf,0,len);catch(Exception e)System.out.println(e.getMessage();編譯運行上面的程序,我們能夠看到當(dāng)前目錄下產(chǎn)生了一個hello.txt的文件,用記事本程序打開這個文件,能看到我們寫入的內(nèi)容。隨后,程序開始讀取文件中的內(nèi)容,并將讀取到的內(nèi)容打印出來。在這個例子中,我們演示了怎樣用FileOutputStream往一個文件中寫東西和怎樣用FileInputStream從一個文件中將內(nèi)容讀出來。有一點不足的是,這兩個類都只提供了對字節(jié)或字節(jié)數(shù)組進行讀取的方法,對于字符串的讀寫,我們還需要進行額外的轉(zhuǎn)換。7.3.4 Reader與WriterJava中的字符是unicode編碼,是雙字節(jié)的,而InputStream與OutputStream是用來處理字節(jié)的,在處理字符文本時不太方便,需要編寫額外的程序代碼。Java為字符文本的輸入輸出專門提供了一套單獨的類,Reader、Writer兩個抽象類與InputStream、OutputStream兩個類相對應(yīng),同樣,Reader、Writer下面也有許多子類,對具體IO設(shè)備進行字符輸入輸出,如FileReader就是用來讀取文件流中的字符。對于Reader和Writer,我們就不過多的說明了,大體的功能和InputStream、OutputStream兩個類相同,但并不是它們的代替者,只是在處理字符串時簡化了我們的編程。我們上面的程序改為使用FileWriter和FileReader來實現(xiàn),修改后的程序代碼如下:import java.io.*;public class FileStreampublic static void main(String args)File f = new File("hello.txt"); tryFileWriter out = new FileWriter(f);out.write("www.it315.org");out.close();catch(Exception e)System.out.println(e.getMessage();tryFileReader in = new FileReader(f);char buf = new char1024;int len = in.read(buf);System.out.println(new String(buf,0,len);catch(Exception e)System.out.println(e.getMessage();我們發(fā)現(xiàn)編譯運行后的結(jié)果與先前沒有什么兩樣,由于FileWriter可以往文件中寫入字符串,我們不用將字符串轉(zhuǎn)換為字節(jié)數(shù)組。相對于FileOutputStream來說,使用FileReader讀取文件中的內(nèi)容,并沒有簡化我們的編程工作,F(xiàn)ileReader的優(yōu)勢,要結(jié)合我們后面講到的包裝類才能體現(xiàn)出來。$獨家見解:我們將程序中的out.close();語句注釋掉后編譯運行,在hello.txt文件中沒有看到out.write語句寫入的字符串,這可能就是我們前面談到的緩沖區(qū)的原因,我們將out.close()改為out.flush后編譯運行,在hello.txt文件中又能夠看到out.write語句寫入的字符串了,這更加證明了FileWriter使用了緩沖區(qū)。在使用FileOutputStream的例子程序中,我們同樣注釋掉out.close();語句,編譯運行后,在hello.txt文件中能夠看到out.write語句寫入的字符串,這說明FileOutputStream沒有使用緩沖區(qū)。7.3.5 PipedInputStream與PipedOutputStream一個PipedInputStream對象必須和一個PipedOutputStream對象進行連接而產(chǎn)生一個通信管道,PipedOutStream可以向管道中寫入數(shù)據(jù),PipedInputStream可以從管道中讀取PipedOutputStream寫入的數(shù)據(jù)。這兩個類主要用來完成線程之間的通信,一個線程的PipedInputStream 對象能夠從另外一個線程的PipedOutputStream對象中讀取數(shù)據(jù)。請看下面的例子:程序清單:PipeStreamTest.javaimport java.io.*;public class PipeStreamTest public static void main(String args) try Thread t1=new Sender(); Thread t2=new Receiver(); PipedOutputStream out = t1.getOutputStream(); PipedInputStream in = t2.getInputStream(); out.connect(in); t1.start(); t2.start(); catch(IOException e) System.out.println(e.getMessage(); class Sender extends Thread private PipedOutputStream out=new PipedOutputStream(); public PipedOutputStream getOutputStream() return out; public void run() String s=new String("hello,receiver ,how are you"); try out.write(s.getBytes(); out.close(); catch(IOException e) System.out.println(e.getMessage(); class Receiver extends Thread private PipedInputStream in=new PipedInputStream(); public PipedInputStream getInputStream() return in; public void run() String s=null; byte buf = new byte1024; try int len =in.read(buf); s = new String(buf,0,len); System.out.println("the following message comes from sender:n"+s); in.close(); catch(IOException e) System.out.println(e.getMessage(); 運行結(jié)果:the following message comes from sender:hello,receiver ,how are youJDK還提供了PipedWriter和PipedReader這兩個類來用于字符文本的管道通信,讀者掌握了PipedOutputStream和PipedInputStream類,自然也就知道如何使用PipedWriter和PipedReader這兩個類了。$獨家見解:使用管道流類,可以實現(xiàn)各個程序模塊之間的松耦合通信,我們可以靈活地將多個這樣的模塊的輸出流與輸入流相連接,以拼裝成滿足各種應(yīng)用的程序,而不用對模塊內(nèi)部進行修改。就象家庭的供水系統(tǒng)一樣,我們可以把進水表的出水管與凈化過濾器的進水管連在一起,然后,把凈化過濾器的出水管同水箱的進水管連在一起來拼湊成我們的供水管道系統(tǒng)。我們可以在這個供水管道系統(tǒng)中增加其他的水處理裝置,也可以更換一個更大的水箱,甚至可以將進水表與水箱直連,而不經(jīng)過凈化過濾器,這一切都只需要各個水處理裝置帶有標(biāo)準(zhǔn)輸入輸出管道??梢?,使用管道流進行通信的模塊具有“強內(nèi)聚,弱耦合”的特點,一個模塊被替換,或被拆卸不會影響其他模塊。假設(shè)有一個使用了管道流的壓縮或加密的模塊,我們的調(diào)用程序只管向該模塊的輸入流中送入數(shù)據(jù),從該模塊的數(shù)據(jù)流中取得數(shù)據(jù),就完成了我們數(shù)據(jù)的壓縮或加密,這個模塊完全就象黑匣子一樣,我們根本不用去了解它的任何細(xì)節(jié)。7.3.6 ByteArrayInputStream與ByteArrayOutputStreamByteArrayInputStream是輸入流的一種實現(xiàn),它有兩個構(gòu)造函數(shù),每個構(gòu)造函數(shù)都需要一個字節(jié)數(shù)組來作為數(shù)據(jù)源:ByteArrayInputStream(byte buf) ByteArrayInputStream(byte buf, int offset, int length)第二個構(gòu)造函數(shù)指定僅使用數(shù)組buf中的從offset開始的length個元素作為數(shù)據(jù)源。ByteArrayOutputStream是輸出流的一種實現(xiàn),它也有兩個構(gòu)造函數(shù)。ByteArrayOutputStream()ByteArrayOutputStream(int)第一種形式的構(gòu)造函數(shù)創(chuàng)建一個32字節(jié)的緩沖區(qū),第二種形式則是根據(jù)參數(shù)指定的大小創(chuàng)建緩沖區(qū),緩沖區(qū)的大小在數(shù)據(jù)過多時能夠自動增長。這兩個流的作用在于,用IO流的方式來完成對字節(jié)數(shù)組內(nèi)容的讀寫。愛思考的讀者一定有過這樣的疑問:對數(shù)組的讀寫非常簡單,我們?yōu)槭裁床恢苯幼x寫字節(jié)數(shù)組呢?我在什么情況下該使用這兩個類呢?有的讀者可能聽說過內(nèi)存虛擬文件或者是內(nèi)存映像文件,它們是把一塊內(nèi)存虛擬成一個硬盤上的文件,原來該寫到硬盤文件上的內(nèi)容會被寫到這個內(nèi)存中,原來該從一個硬盤文件上讀取內(nèi)容可以改為從內(nèi)存中直接讀取。如果程序在運行過程中要產(chǎn)生一些臨時文件,就可以用虛擬文件的方式來實現(xiàn),我們不用訪問硬盤,而是直接訪問內(nèi)存,會提高應(yīng)用程序的效率。假設(shè)有一個別人已經(jīng)寫好了的壓縮函數(shù),這個函數(shù)接收兩個參數(shù),一個輸入流對象,一個輸出流對象,它從輸入流對象中讀取數(shù)據(jù),并將壓縮后的結(jié)果寫入輸出流對象中。我們的程序要將一臺計算機的屏幕圖像通過網(wǎng)絡(luò)不斷地傳送到另外的計算機上,為了節(jié)省網(wǎng)絡(luò)帶寬,我們需要對一副屏幕圖像的像素數(shù)據(jù)進行壓縮后,再通過網(wǎng)絡(luò)發(fā)送出去的。如果沒有內(nèi)存虛擬文件,我們就必須先將一副屏幕圖像的像素數(shù)據(jù)寫入到硬盤上的一個臨時文件,再以這個文件作為輸入流對象去調(diào)用那個壓縮函數(shù),接著又從壓縮函數(shù)生成的壓縮文件中讀取壓縮后的數(shù)據(jù),再通過網(wǎng)絡(luò)發(fā)送出去,最后刪除壓縮前后所生成的兩個臨時文件??梢娺@樣的效率是非常低的。我們要在程序分配一個存儲數(shù)據(jù)的內(nèi)存塊,通常都用定義一個字節(jié)數(shù)組來實現(xiàn)的,JDK中提供了ByteArrayInputStream和ByteArrayOutputStream這兩個類可實現(xiàn)類似內(nèi)存虛擬文件的功能,我們將抓取到的計算機屏幕圖像的所有像素數(shù)據(jù)保存在一個數(shù)組中,然后根據(jù)這個數(shù)組創(chuàng)建一個ByteArrayInputStream流對象,同時創(chuàng)建一個用于保存壓縮結(jié)果的ByteArrayOutputStream流對象,將這兩個對象作為參數(shù)傳遞給壓縮函數(shù),最后從ByteArrayOutputStream流對象中返回包含有壓縮結(jié)果的數(shù)組。我們通過下面的例子程序來模擬上面的過程,我們并沒有真正壓縮輸入流中的內(nèi)容,只是把輸入流中的所有英文字母變成對應(yīng)的大寫字母寫入到輸出流中。程序清單:ByteArrayTest.javaimport java.io.*;public class ByteArrayTestpublic static void main(String args) throws ExceptionString tmp="abcdefghijklmnopqrstuvwxyz"byte src =tmp.getBytes();/src為轉(zhuǎn)換前的內(nèi)存塊ByteArrayInputStream input = new ByteArrayInputStream(src);ByteArrayOutputStream output = new ByteArrayOutputStream();new ByteArrayTest().transform(input,output);byte result = output.toByteArray();/result為轉(zhuǎn)換后的內(nèi)存塊System.out.println(new String(result);public void transform(InputStream in,OutputStream out)int c=0;trywhile(c=in.read()!=-1)/read在讀到流的結(jié)尾處返回-1int C = (int)Character.toUpperCase(char)c);out.write(C);catch(Exception e)e.printStackTrace();運行結(jié)果為:ABCDEFGHIJKLMNOPQRSTUVWXYZ與ByteArrayInputStream和ByteArrayOutputStream類對應(yīng)的字符串讀寫類分別是StringReader和StringWriter。讀者可以將上面的程序修改成由這兩個類來完成,具體的程序代碼就不在這里多說了。7.3.7 IO程序代碼的復(fù)用由于沒有編碼為-1的字符,所以,操作系統(tǒng)就使用-1作為硬盤上的每個文件的結(jié)尾標(biāo)記,對于文本文件,我們的程序只要從文件中讀取到了一個-1的字符值時,就可以確定已經(jīng)到了這個文件結(jié)尾。注意,這種方式只能用于判斷文本文件是否結(jié)束,不能判斷一個二進制文件是否結(jié)束。盡管二進值文件的結(jié)尾標(biāo)記也是-1,因為二進制文件中的每個字節(jié)可以是-128到127之間的任意取值,其中就包括-1,當(dāng)程序讀取到一個-1的字節(jié)是,就難以判定是文件結(jié)尾還是文件中的有效數(shù)據(jù)。對于標(biāo)準(zhǔn)的二進值文件,在文件開始部分,都有一個文件頭指定文件的大小,程序就是憑借文件頭中的這個大小來讀取文件中的所有內(nèi)容的。我本人曾經(jīng)為二進制文件和文本文件的區(qū)別困惑過很久,后來發(fā)現(xiàn)許多有一定軟件開發(fā)經(jīng)驗的人也沒完全搞清楚兩者的區(qū)別。我們知道內(nèi)存中的一個字節(jié)中數(shù)據(jù)可以是-128到127之間的任意值,實際上是以二進制形式存放的,文件就是一片內(nèi)存的數(shù)據(jù)在硬盤上的另外一種存放形式,也都是二進制數(shù)據(jù),所以,可以說每個文件都是二進制的。我們現(xiàn)在的每個字符由一個或多個字節(jié)組成,每個字節(jié)都是用的-128到127之間的部分?jǐn)?shù)值來表示的,也就是說,-128到127之間還有一些數(shù)據(jù)沒有對應(yīng)任何字符的任何字節(jié)。如果一個文件中的每個字節(jié)中的內(nèi)容都是可以表示成字符的數(shù)據(jù),我們就可以稱這個文件為文本文件,可見,文本文件只是二進制文件中的一種特例,為了與文本文件相區(qū)別,人們又把除了文本文件以外的文件稱之為二進制文件。由于很難嚴(yán)格區(qū)分文本文件和二進制文件的概念,所以我們可以簡單地認(rèn)為,如果一個文件專用于存儲文本字符的數(shù)據(jù),沒有包含字符之外的其他數(shù)據(jù),我們就稱之為文本文件,除此之外的文件就是二進制文件。為了支持標(biāo)準(zhǔn)輸入輸出設(shè)備,Java定義了兩個特殊的流對象,System.in和System.out。System.in對應(yīng)鍵盤,是InputStream類型的,程序使用System.in可以讀取從鍵盤上輸入的數(shù)據(jù)。System.out對應(yīng)顯示器,是PrintStream類型的,PrintStream是OutputStream的一個子類,程序使用System.out可以將數(shù)據(jù)輸出到顯示器上。鍵盤可以被當(dāng)作一個特殊的輸入文件,顯示器可以被當(dāng)作一個特殊的輸出文件。當(dāng)我們把鍵盤作為輸入文件處理時,在 Windows下,我們可以按下ctrl+z組合鍵來輸入-1作為文件結(jié)束標(biāo)記,在linux下,我們可以按下ctrl+d組合鍵來輸入-1。我們在編寫流的程序時,應(yīng)盡量考慮到程序代碼的復(fù)用性,對于我們上面的程序代碼,我們可以直接調(diào)用上面的transform方法,將鍵盤上輸入的內(nèi)容轉(zhuǎn)變成大寫字母后打印在屏幕上,程序代碼如下:import java.io.*;public class ByteArrayTestpublic static void main(String args) throws Exceptionnew ByteArrayTest().transform(System.in,System.out);public void transform(InputStream in,OutputStream out)int c=0;trywhile(c=in.read()!=-1)int C = (int)Character.toUpperCase(char)c);out.write(C);catch(Exception e)e.printStackTrace();我們沒有修改transform方法中的任何代碼,就利用它完成了我們期望的功能。我們還可以使用transform方法將一個文件中的內(nèi)容全部變成大寫字母后寫入另外一個文件,也可以將鍵盤上輸入的內(nèi)容轉(zhuǎn)變成大寫字母后寫入另外一個文件,這就是因為我們在tranform方法中使用的是InputStream和OutputStream這兩個抽象基類,而不是直接使用某個具體的子類,這樣就達到了以不變應(yīng)萬變的效果。如果我們平時從鍵盤上讀取內(nèi)容的程序代碼也放在一個類似transform方法的函數(shù)中去完成,也是用-1來作為鍵盤輸入的結(jié)束,在該函數(shù)中不直接使用System.in,只是在調(diào)用該函數(shù)時,將System.in作為參數(shù)傳遞進去。這樣,我們以后要從某個文件中讀取數(shù)據(jù),來代替手工鍵盤輸入時,我們可以直接使用這個函數(shù),程序就不用作太多的修改了。7.4 過濾流與包裝類7.4.1 理解包裝類的概念與作用在前面的部分,我們接觸到了許多節(jié)點流類,就以FileOutputStream和FileInputStream為例吧,這兩個類只提供了讀寫字節(jié)的方法,我們通過它們只能往文件中寫入字節(jié)或從文件中讀取字節(jié)。在實際應(yīng)用中,我們要往文件中寫入或讀取各種類型的數(shù)據(jù),我們就必須先將其他類型的數(shù)據(jù)轉(zhuǎn)換成字節(jié)數(shù)組后寫入文件或是將從文件中讀取到的字節(jié)數(shù)組轉(zhuǎn)換成其他類型,這給我們的程序帶來了一些困難和麻煩。如果有人給我們提供了一個中間類,這個中間類提供了讀寫各種類型的數(shù)據(jù)的各種方法,當(dāng)我們需要寫入其他類型的數(shù)據(jù)時,只要調(diào)用中間類中的對應(yīng)的方法即可,在這個中間類的方法內(nèi)部,它將其他數(shù)據(jù)類型轉(zhuǎn)換成字節(jié)數(shù)組,然后調(diào)用底層的節(jié)點流類將這個字節(jié)數(shù)組寫入目標(biāo)設(shè)備。我們將這個中間類叫做過濾流類或處理流類,也叫包裝類,如IO包中有一個叫DataOutputStream的包裝類,下面是它所提供的部分方法的列表。public final void writeBoolean(boolean v) throws IOExceptionpublic final void writeShort(int v) throws IOExceptionpublic final void writeChar(int v) throws IOExceptionpublic final void writeInt(int v) throws IOExceptionpublic final void writeLong(long v) throws IOExceptionpublic final void writeFloat(float v) throws IOExceptionpublic final void writeDouble(double v) throws IOExceptionpublic final void writeBytes(String s) throws IOException大家從上面的方法名和參數(shù)類型中,就知道這個包裝類能幫我們往IO設(shè)備中寫入各種類型的數(shù)據(jù)。包裝類的調(diào)用過程如圖7.2所示:圖7.2使用輸出包裝類的過程,就好比我們要給某個市長送禮,該市長向來不接受陌生人的禮品,但其夫人則是來者不拒的。我們只要將禮品送到市長夫人手中,就等于送到了市長的手中。市長夫人就是我們用到的輸出包裝類。使用輸入包裝類過程,好比我們要想借用市長大人的一點權(quán)力,承攬一個假競標(biāo)的工程項目,我們是沒法直接請動市長大人來替我們說話的,但我們可以讓市長公子來替我們辦好這件事,我們就間接借用了市長的權(quán)力。市長公子就是我們用到的輸入包裝類。我們還可以用包裝類去包裝另外一個包裝類,創(chuàng)建包裝類對象時,必須指定它要調(diào)用的那個底層流對象,也就是這些包裝類的構(gòu)造函數(shù)中,都必須接收另外一個流對象作為參數(shù)。如DataOutputStream包裝類的構(gòu)造函數(shù)為:public DataOutputStream(OutputStream out)參數(shù)out就是DataOutputStream要調(diào)用的那個底層輸出流對象。7.4.2 BufferedInputStream與 BufferedOuputStream對I/O進行緩沖是一種常見的性能優(yōu)化。緩沖流為I/O流增加了內(nèi)存緩沖區(qū)。增加緩沖區(qū)有兩個基本目的:ü 允許Java的I/O一次不只操作一個字節(jié),這樣提高了整個系統(tǒng)的性能。ü 由于有緩沖區(qū),使得在流上執(zhí)行skip、mark和reset方法都成為可能。BufferedInputStreamJava的BufferedInputStream類可以對任何的InputStream進行帶緩沖區(qū)的封裝以達到性能的改善。BufferedInputStream有兩個構(gòu)造函數(shù):BufferedInputStream(InputStream in)BufferedInputStream(InputStream in,int size)第一種形式的構(gòu)造函數(shù)創(chuàng)建了一個

注意事項

本文([Java就業(yè)培訓(xùn)教程] 第七章 IO)為本站會員(z***)主動上傳,裝配圖網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對上載內(nèi)容本身不做任何修改或編輯。 若此文所含內(nèi)容侵犯了您的版權(quán)或隱私,請立即通知裝配圖網(wǎng)(點擊聯(lián)系客服),我們立即給予刪除!

溫馨提示:如果因為網(wǎng)速或其他原因下載失敗請重新下載,重復(fù)下載不扣分。




關(guān)于我們 - 網(wǎng)站聲明 - 網(wǎng)站地圖 - 資源地圖 - 友情鏈接 - 網(wǎng)站客服 - 聯(lián)系我們

copyright@ 2023-2025  zhuangpeitu.com 裝配圖網(wǎng)版權(quán)所有   聯(lián)系電話:18123376007

備案號:ICP2024067431-1 川公網(wǎng)安備51140202000466號


本站為文檔C2C交易模式,即用戶上傳的文檔直接被用戶下載,本站只是中間服務(wù)平臺,本站所有文檔下載所得的收益歸上傳人(含作者)所有。裝配圖網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對上載內(nèi)容本身不做任何修改或編輯。若文檔所含內(nèi)容侵犯了您的版權(quán)或隱私,請立即通知裝配圖網(wǎng),我們立即給予刪除!