Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream
Java IO流學(xué)習(xí)總結(jié)三:緩沖流-BufferedInputStream、BufferedOutputStream
轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/zhaoyanjun6/article/details/54894451
本文出自【趙彥軍的博客】
InputStream
|__FilterInputStream|__BufferedInputStream 首先拋出一個問題,有了InputStream為什么還要有BufferedInputStream?
BufferedInputStream和BufferedOutputStream這兩個類分別是FilterInputStream和FilterOutputStream的子類,作為裝飾器子類,使用它們可以防止每次讀取/發(fā)送數(shù)據(jù)時進(jìn)行實際的寫操作,代表著使用緩沖區(qū)。
我們有必要知道不帶緩沖的操作,每讀一個字節(jié)就要寫入一個字節(jié),由于涉及磁盤的IO操作相比內(nèi)存的操作要慢很多,所以不帶緩沖的流效率很低。帶緩沖的流,可以一次讀很多字節(jié),但不向磁盤中寫入,只是先放到內(nèi)存里。等湊夠了緩沖區(qū)大小的時候一次性寫入磁盤,這種方式可以減少磁盤操作次數(shù),速度就會提高很多!
同時正因為它們實現(xiàn)了緩沖功能,所以要注意在使用BufferedOutputStream寫完數(shù)據(jù)后,要調(diào)用flush()方法或close()方法,強行將緩沖區(qū)中的數(shù)據(jù)寫出。否則可能無法寫出數(shù)據(jù)。與之相似還BufferedReader和BufferedWriter兩個類。
現(xiàn)在就可以回答在本文的開頭提出的問題:
BufferedInputStream和BufferedOutputStream類就是實現(xiàn)了緩沖功能的輸入流/輸出流。使用帶緩沖的輸入輸出流,效率更高,速度更快。
總結(jié):
BufferedInputStream是緩沖輸入流。它繼承于FilterInputStream。BufferedInputStream的作用是為另一個輸入流添加一些功能,例如,提供“緩沖功能”以及支持mark()標(biāo)記和reset()重置方法。BufferedInputStream本質(zhì)上是通過一個內(nèi)部緩沖區(qū)數(shù)組實現(xiàn)的。例如,在新建某輸入流對應(yīng)的BufferedInputStream后,當(dāng)我們通過read()讀取輸入流的數(shù)據(jù)時,BufferedInputStream會將該輸入流的數(shù)據(jù)分批的填入到緩沖區(qū)中。每當(dāng)緩沖區(qū)中的數(shù)據(jù)被讀完之后,輸入流會再次填充數(shù)據(jù)緩沖區(qū);如此反復(fù),直到我們讀完輸入流數(shù)據(jù)位置。
BufferedInputStream API簡介
源碼關(guān)鍵字段分析
private static int defaultBufferSize = 8192;//內(nèi)置緩存字節(jié)數(shù)組的大小 8KBprotected volatile byte buf[]; //內(nèi)置緩存字節(jié)數(shù)組protected int count; //當(dāng)前buf中的字節(jié)總數(shù)、注意不是底層字節(jié)輸入流的源中字節(jié)總數(shù)protected int pos; //當(dāng)前buf中下一個被讀取的字節(jié)下標(biāo)protected int markpos = -1; //最后一次調(diào)用mark(int readLimit)方法記錄的buf中下一個被讀取的字節(jié)的位置protected int marklimit; //調(diào)用mark后、在后續(xù)調(diào)用reset()方法失敗之前云尋的從in中讀取的最大數(shù)據(jù)量、用于限制被標(biāo)記后buffer的最大值
構(gòu)造函數(shù)
BufferedInputStream(InputStream in) //使用默認(rèn)buf大小、底層字節(jié)輸入流構(gòu)建bis BufferedInputStream(InputStream in, int size) //使用指定buf大小、底層字節(jié)輸入流構(gòu)建bis 一般方法介紹
int available(); //返回底層流對應(yīng)的源中有效可供讀取的字節(jié)數(shù) void close(); //關(guān)閉此流、釋放與此流有關(guān)的所有資源 boolean markSupport(); //查看此流是否支持markvoid mark(int readLimit); //標(biāo)記當(dāng)前buf中讀取下一個字節(jié)的下標(biāo) int read(); //讀取buf中下一個字節(jié) int read(byte[] b, int off, int len); //讀取buf中下一個字節(jié) void reset(); //重置最后一次調(diào)用mark標(biāo)記的buf中的位子 long skip(long n); //跳過n個字節(jié)、 不僅僅是buf中的有效字節(jié)、也包括in的源中的字節(jié) BufferedOutputStream API簡介
關(guān)鍵字段
protected byte[] buf; //內(nèi)置緩存字節(jié)數(shù)組、用于存放程序要寫入out的字節(jié) protected int count; //內(nèi)置緩存字節(jié)數(shù)組中現(xiàn)有字節(jié)總數(shù) 構(gòu)造函數(shù)
BufferedOutputStream(OutputStream out); //使用默認(rèn)大小、底層字節(jié)輸出流構(gòu)造bos。默認(rèn)緩沖大小是 8192 字節(jié)( 8KB )BufferedOutputStream(OutputStream out, int size); //使用指定大小、底層字節(jié)輸出流構(gòu)造bos 構(gòu)造函數(shù)源碼:
/*** Creates a new buffered output stream to write data to the* specified underlying output stream.* @param out the underlying output stream.*/public BufferedOutputStream(OutputStream out) {this(out, 8192);}/*** Creates a new buffered output stream to write data to the* specified underlying output stream with the specified buffer* size.** @param out the underlying output stream.* @param size the buffer size.* @exception IllegalArgumentException if size <= 0.*/public BufferedOutputStream(OutputStream out, int size) {super(out);if (size <= 0) {throw new IllegalArgumentException("Buffer size <= 0");}buf = new byte[size];} 一般方法
//在這里提一句,`BufferedOutputStream`沒有自己的`close`方法,當(dāng)他調(diào)用父類`FilterOutputStrem`的方法關(guān)閉時,會間接調(diào)用自己實現(xiàn)的`flush`方法將buf中殘存的字節(jié)flush到out中,再`out.flush()`到目的地中,DataOutputStream也是如此。 void flush(); 將寫入bos中的數(shù)據(jù)flush到out指定的目的地中、注意這里不是flush到out中、因為其內(nèi)部又調(diào)用了out.flush() write(byte b); 將一個字節(jié)寫入到buf中 write(byte[] b, int off, int len); 將b的一部分寫入buf中 那么什么時候flush()才有效呢?
答案是:當(dāng)OutputStream是BufferedOutputStream時。
當(dāng)寫文件需要flush()的效果時,需要
FileOutputStream fos = new FileOutputStream("c:\a.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
也就是說,需要將FileOutputStream作為BufferedOutputStream構(gòu)造函數(shù)的參數(shù)傳入,然后對BufferedOutputStream進(jìn)行寫入操作,才能利用緩沖及flush()。
查看BufferedOutputStream的源代碼,發(fā)現(xiàn)所謂的buffer其實就是一個byte[]。
BufferedOutputStream的每一次write其實是將內(nèi)容寫入byte[],當(dāng)buffer容量到達(dá)上限時,會觸發(fā)真正的磁盤寫入。
而另一種觸發(fā)磁盤寫入的辦法就是調(diào)用flush()了。
1.BufferedOutputStream在close()時會自動flush
2.BufferedOutputStream在不調(diào)用close()的情況下,緩沖區(qū)不滿,又需要把緩沖區(qū)的內(nèi)容寫入到文件或通過網(wǎng)絡(luò)發(fā)送到別的機器時,才需要調(diào)用flush.
實戰(zhàn)演練1:復(fù)制文件.
操作:使用緩存流將F盤根目錄里面名字為:123.png 圖片復(fù)制成 abc.png
package com.app;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;public class A3 {public static void main(String[] args) throws IOException {String filePath = "F:/123.png" ;String filePath2 = "F:/abc.png" ;File file = new File( filePath ) ;File file2 = new File( filePath2 ) ;copyFile( file , file2 );}/*** 復(fù)制文件* @param oldFile* @param newFile*/public static void copyFile( File oldFile , File newFile){InputStream inputStream = null ;BufferedInputStream bufferedInputStream = null ;OutputStream outputStream = null ;BufferedOutputStream bufferedOutputStream = null ;try {inputStream = new FileInputStream( oldFile ) ;bufferedInputStream = new BufferedInputStream( inputStream ) ;outputStream = new FileOutputStream( newFile ) ;bufferedOutputStream = new BufferedOutputStream( outputStream ) ;byte[] b=new byte[1024]; //代表一次最多讀取1KB的內(nèi)容int length = 0 ; //代表實際讀取的字節(jié)數(shù)while( (length = bufferedInputStream.read( b ) )!= -1 ){//length 代表實際讀取的字節(jié)數(shù)bufferedOutputStream.write(b, 0, length );}//緩沖區(qū)的內(nèi)容寫入到文件bufferedOutputStream.flush();} catch (FileNotFoundException e) {e.printStackTrace();}catch (IOException e) {e.printStackTrace();}finally {if( bufferedOutputStream != null ){try {bufferedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}if( bufferedInputStream != null){try {bufferedInputStream.close();} catch (IOException e) {e.printStackTrace();}}if( inputStream != null ){try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if ( outputStream != null ) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}
}
效果圖:
如何正確的關(guān)閉流
在上面的代碼中,我們關(guān)閉流的代碼是這樣寫的。
finally {if( bufferedOutputStream != null ){try {bufferedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}if( bufferedInputStream != null){try {bufferedInputStream.close();} catch (IOException e) {e.printStackTrace();}}if( inputStream != null ){try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if ( outputStream != null ) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}} 思考:在處理流關(guān)閉完成后,我們還需要關(guān)閉節(jié)點流嗎?
讓我們帶著問題去看源碼:
bufferedOutputStream.close();
/*** Closes this input stream and releases any system resources* associated with the stream.* Once the stream has been closed, further read(), available(), reset(),* or skip() invocations will throw an IOException.* Closing a previously closed stream has no effect.** @exception IOException if an I/O error occurs.*/public void close() throws IOException {byte[] buffer;while ( (buffer = buf) != null) {if (bufUpdater.compareAndSet(this, buffer, null)) {InputStream input = in;in = null;if (input != null)input.close();return;}// Else retry in case a new buf was CASed in fill()}} close()方法的作用
1、關(guān)閉輸入流,并且釋放系統(tǒng)資源
2、BufferedInputStream裝飾一個 InputStream 使之具有緩沖功能,is要關(guān)閉只需要調(diào)用最終被裝飾出的對象的 close()方法即可,因為它最終會調(diào)用真正數(shù)據(jù)源對象的 close()方法。因此,可以只調(diào)用外層流的close方法關(guān)閉其裝飾的內(nèi)層流。
那么如果我們想逐個關(guān)閉流,我們該怎么做?
答案是:先關(guān)閉外層流,再關(guān)閉內(nèi)層流。一般情況下是:先打開的后關(guān)閉,后打開的先關(guān)閉;另一種情況:看依賴關(guān)系,如果流a依賴流b,應(yīng)該先關(guān)閉流a,再關(guān)閉流b。例如處理流a依賴節(jié)點流b,應(yīng)該先關(guān)閉處理流a,再關(guān)閉節(jié)點流b
看懂了怎么正確的關(guān)閉流之后,那么我們就可以優(yōu)化上面的代碼了,只關(guān)閉外層的處理流。
finally {if( bufferedOutputStream != null ){try {bufferedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}if( bufferedInputStream != null){try {bufferedInputStream.close();} catch (IOException e) {e.printStackTrace();}}} 個人微信號:zhaoyanjun125 , 歡迎關(guān)注
轉(zhuǎn)載于:https://www.cnblogs.com/zhaoyanjun/p/6376937.html
總結(jié)
以上是生活随笔為你收集整理的Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求一个霹雳里好听的名字!
- 下一篇: 谁能告诉我这是什么呀?有懂韩语的吗?