Java 缓冲流简介及简单用法
在java編程中, 我們有時(shí)會(huì)聽(tīng)到緩沖流和原始流等字眼.
其實(shí)在之前的博文中, 提到過(guò)流可以分為原始流和處理流.
http://blog.csdn.net/nvd11/article/details/30126233
也就是說(shuō)處理流是包裹在原始流對(duì)原始流的數(shù)據(jù)進(jìn)行進(jìn)一步的處理, 這時(shí)的流就有兩層了.
而緩沖流就是處理流的一種.
一, 緩沖流的定義
緩沖流是處理流的一種, 它依賴(lài)于原始的輸入輸出流, 它令輸入輸出流具有1個(gè)緩沖區(qū), 顯著減少與外部設(shè)備的IO次數(shù), 而且提供一些額外的方法.
可見(jiàn), 緩沖流最大的特點(diǎn)就是具有1個(gè)緩沖區(qū)! 而我們使用緩沖流無(wú)非兩個(gè)目的:
1. 減少I(mǎi)O次數(shù)(提升performance)
2. 使用一些緩沖流的額外的方法.
二, 不使用緩沖流的例子
下面例子, 將1個(gè)大小為32MB的音樂(lè)文件復(fù)制到另1個(gè)地方. (/home/gateman/Music/Nickelback - Rockstar.flac 復(fù)制到 /home/gateman/tmp/Rockstar.flac
而且只是用原始的 FileInputStream 和 FileOutputStream, 1個(gè)1個(gè)字節(jié)的讀取寫(xiě)入.
package Stream_kng.BufferStream_kng;import java.io.*;public class Stream2{public static void f(){FileInputStream fi = null;try{fi = new FileInputStream("/home/gateman/Music/Nickelback - Rockstar.flac");}catch(FileNotFoundException e){System.out.println("File not found!");}FileOutputStream fo = null;try{fo = new FileOutputStream("/home/gateman/tmp/Rockstar.flac");}catch(Exception e){System.out.println("error in file output stream's creation");e.printStackTrace();}int byt;try{byt = fi.read();while(-1 != byt){fo.write(byt);byt = fi.read();}}catch(IOException e){e.printStackTrace();}try{fo.flush();}catch(IOException e){System.out.println("Exception in flush()");}finally{if (null != fo){try{fo.close();}catch(IOException e){System.out.println("Exception in fo.close()");}}if (null != fi){try{fi.close();}catch(IOException e){System.out.println("Exception in fi.close()");}}}System.out.println("copy done!");} }
我們看看編譯后的執(zhí)行效果:
可見(jiàn)復(fù)制區(qū)區(qū)1個(gè)32m的文件都用了將近1分鐘,? 不可以接受啊.
原因就是輸入流fi 和 輸出流都是1個(gè)1個(gè)字節(jié)的讀寫(xiě), 也就是說(shuō)這個(gè)程序?qū)τ脖P(pán)讀了 30000多次, 寫(xiě)了30000多次.
而硬盤(pán)IO是整個(gè)計(jì)算機(jī)最慢的動(dòng)作, 所以我們需要減少外部設(shè)備的IO.
方法很簡(jiǎn)單, 就是增加每一次IO的數(shù)據(jù)量(緩沖), 自然就是減少了IO次數(shù).
三, 使用緩沖流的例子
上面的代碼只有兩個(gè)流.輸入流fi 和 輸出流.
下面修改一下, 增加兩個(gè)緩沖流, 分別包裹著原始的輸入輸出流.
package Stream_kng.BufferStream_kng;import java.io.*;public class BufferStream1{public static void f(){FileInputStream fi = null;try{fi = new FileInputStream("/home/gateman/Music/Nickelback - Rockstar.flac");}catch(FileNotFoundException e){System.out.println("File not found!");}FileOutputStream fo = null;try{fo = new FileOutputStream("/home/gateman/tmp/Rockstar.flac");}catch(Exception e){System.out.println("error in file output stream's creation");e.printStackTrace();}//bufferStreamBufferedInputStream bis = new BufferedInputStream(fi,512);BufferedOutputStream bos = new BufferedOutputStream(fo,512);int byt;try{byt = bis.read();while(-1 != byt){bos.write(byt);byt = bis.read();}}catch(IOException e){e.printStackTrace();}try{bos.flush();}catch(IOException e){System.out.println("Exception in flush()");}finally{if (null != bos){try{bos.close();}catch(IOException e){System.out.println("Exception in bos.close()");}}if (null != bis){try{bis.close();}catch(IOException e){System.out.println("Exception in bis.close()");}}}System.out.println("copy done!!");} }分析上面代碼,?
這兩句就建立了兩個(gè)緩沖流對(duì)象, 分別包裹了 原始的輸入流fi 和 原始的輸出流fo. 并指定緩沖區(qū)的大小未512kb
后面就只調(diào)用緩沖流的read()和write方法.
代碼看起來(lái)還是1個(gè)1個(gè)字節(jié)地讀寫(xiě)的, 跟第二節(jié)例子很類(lèi)似.
但是執(zhí)行效果大大不同:
gateman@TPEOS classes $ ls -lh /home/gateman/tmp/Rockstar.flac ls: cannot access /home/gateman/tmp/Rockstar.flac: No such file or directory gateman@TPEOS classes $ time java Enter_1 copy done!!real 0m1.766s user 0m1.612s sys 0m0.168s gateman@TPEOS classes $ ls -lh /home/gateman/tmp/Rockstar.flac -rw-rw-r-- 1 gateman gateman 32M Jul 1 23:19 /home/gateman/tmp/Rockstar.flac可見(jiàn)這次執(zhí)行了1.766秒, 相比之前的57多秒簡(jiǎn)直不可以同日而語(yǔ).
原因就是緩沖輸入流會(huì)預(yù)讀到512k(緩沖區(qū)大小)字節(jié)才發(fā)給程序, 緩沖輸出流寫(xiě)滿(mǎn)緩沖區(qū)(512k)才寫(xiě)入外部設(shè)備.
如下圖:
四, 緩沖流輸入流 BufferedInputStream流的常用方法介紹
上面程序使用了兩個(gè)緩沖流:
BufferedInputStream 和 BufferedOutputStream.
分別對(duì)應(yīng)輸入和輸出.
其實(shí)它們的機(jī)制類(lèi)似.
這里只介紹緩沖輸入流的常用方法:
4.1 new BufferedInputStream(InputStream is, int bufferSize)
這個(gè)是緩沖輸入流最常用的構(gòu)造方法.
它有兩個(gè)參數(shù), 第一個(gè)就是要包裹的輸入流,? 其中InputStream是1個(gè)抽象類(lèi), 實(shí)際上我們傳送的是其子類(lèi)(例如上面例子的FileInputStream)的對(duì)象, 這里用到多態(tài)的知識(shí).
第二個(gè)參數(shù)也很重要, 就是制定緩沖流緩沖區(qū)的初始大小. 單位是kb 上面的例子我們指定為512kb.
注意構(gòu)造方法并不需要強(qiáng)制捕捉異常.
4.2 int read() throws IOException
讀取一個(gè)字節(jié)放入緩沖區(qū), 用法與InputStream的read()基本一樣的.
4.3 int read(byte[] bytArr) throws IOException
讀取若干個(gè)字節(jié)放入字節(jié)數(shù)組bytArr, 返回實(shí)際讀取的字節(jié)個(gè)數(shù), 用法與InputStream的同名同參方法基本一樣.
4.4 void mark(int readlimit) 和 void reset() throws IOException
這個(gè)方法就是緩沖輸入流額外提供的方法.
它的作用就是在當(dāng)前位置作1個(gè)標(biāo)記,? 它允許調(diào)用另1個(gè)方法reset() 令到流重新定位到這個(gè)標(biāo)記上.
有點(diǎn)類(lèi)似于oracle 的transation 的savepoint 和 rollback
它有什么意義?
它可以令我們?cè)诹髦卸?個(gè)標(biāo)記, 然后讀取標(biāo)記后的若干數(shù)據(jù)作判斷or處理, 如果符合某一條件可以返回到標(biāo)記位置重新讀取...
它的參數(shù) int readlimit?
意思是, 調(diào)用reset()前允許讀取的字節(jié)個(gè)個(gè)數(shù),? 更通俗地說(shuō), 一旦你標(biāo)記后讀取超過(guò)了readlimit個(gè)字節(jié), 那么就不可再調(diào)用reset()了, 會(huì)拋異常.
而且, 這些操作都是基于緩沖區(qū)內(nèi)處理的, 所以標(biāo)記后讀取的字節(jié)數(shù)也不能大于緩沖區(qū)大小再調(diào)用reset(), 所以 readlimit設(shè)置大于緩沖區(qū)是沒(méi)有意義的.
4.5 void close() throws IOException
上面的例子有4個(gè)流, 兩個(gè)原始流, 兩個(gè)緩沖流, 但是到了代碼的最后只關(guān)閉了兩個(gè)緩沖流.
實(shí)際上, java中關(guān)閉1個(gè)處理流, 會(huì)自動(dòng)調(diào)用處理流包裹的原始流的close()方法, 也就是說(shuō)回嵌套地關(guān)閉流, 所以只需要關(guān)閉緩沖流, 不需關(guān)閉原始流, 否則拋異常!
五, 不使用緩沖流的緩沖機(jī)制.
實(shí)際上很多人都明白, 基本的原始流InputStream 有1個(gè)方法
int read(byte[]) 一次性地讀取多個(gè)字節(jié).
這個(gè)byte[] 字節(jié)數(shù)組實(shí)際上也是1個(gè)緩沖區(qū).
下面例子:
package Stream_kng.BufferStream_kng;import java.io.*;public class Stream3{public static void f(){FileInputStream fi = null;try{fi = new FileInputStream("/home/gateman/Music/Nickelback - Rockstar.flac");}catch(FileNotFoundException e){System.out.println("File not found!");}FileOutputStream fo = null;try{fo = new FileOutputStream("/home/gateman/tmp/Rockstar.flac");}catch(Exception e){System.out.println("error in file output stream's creation");e.printStackTrace();}byte[] byteArr = new byte[512]; //use a buffer instead of a byteint len;try{len = fi.read(byteArr);while(-1 != len){fo.write(byteArr,0,len);len = fi.read(byteArr);}}catch(IOException e){e.printStackTrace();}try{fo.flush();}catch(IOException e){System.out.println("Exception in flush()");}finally{if (null != fo){try{fo.close();}catch(IOException e){System.out.println("Exception in fo.close()");}}if (null != fi){try{fi.close();}catch(IOException e){System.out.println("Exception in fi.close()");}}}System.out.println("copy done!!");} }上面例子沒(méi)有使用緩沖流, 只有兩個(gè)基本的字節(jié)輸入輸入流.
但是每一次讀寫(xiě)都是利用到緩沖字節(jié)數(shù)組 bytArr.
實(shí)際上的效果也起到了緩沖作用, 緩沖的大小就是字節(jié)數(shù)組的大小(例子中是512k)
執(zhí)行效果
實(shí)際效果更快了, 只需0.246s
貌似比使用緩沖流效果更好啊.
那么為何還需要緩沖流呢?
答案就是 緩沖流有預(yù)讀機(jī)制,比起使用緩沖數(shù)組的緩沖效果更加明顯, 如果處理一些大數(shù)據(jù)文件, 或者網(wǎng)絡(luò)傳輸, 使用緩沖流的效果會(huì)更加好!
六, 總結(jié)
讀到這里, 大家應(yīng)該知道緩沖流的意義就是緩沖數(shù)據(jù).
實(shí)際上很多常用的程序都有用到緩沖流的技術(shù), 例如迅雷下載, 有緩沖區(qū)設(shè)置, 下載一定量的數(shù)據(jù)到內(nèi)存, 然后一次過(guò)寫(xiě)入到硬盤(pán), 就大大減少了寫(xiě)硬盤(pán)的次數(shù).
還有在線視頻的緩沖, 都是差不多的機(jī)制.
總結(jié)
以上是生活随笔為你收集整理的Java 缓冲流简介及简单用法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java Stream(流)的分类, 四
- 下一篇: 数据流DataInput(Output)