BufferedOutputStream是緩衝字節輸出流,繼承自FilterOutputStream,它經過在內部建立一個緩衝區緩存寫入底層輸出流的字節數據,每次向底層字節輸出流寫入數據時不是當即寫入而是先寫入到緩衝區等到緩衝區已滿或者達到限定條件再將緩衝區中的字節數據真正寫入底層字節輸出流,這樣能夠避免每次寫入都要進行一次底層調用,在沒有數據實時寫入需求的場景下能夠提升性能;java
public class BufferedOutputStream extends FilterOutputStream { /** * 保存字節數據的內部緩衝區 */ protected byte buf[]; /** * 緩衝區中的有效字節數,在0~buf.length範圍內,也能夠視爲緩衝區數組下一個字節的寫入位置 */ protected int count; }
BufferedOutputStream繼承自FilterOutputStream,基於裝飾器模式能夠使用它包裝其餘字節輸出流爲它們提供數據寫入緩衝的功能,類內部的成員變量有包裝的底層字節輸出流對象out,內部緩衝區buf和統計緩衝區有效字節個數的變量count。數組
/** * 構造函數,指定底層字節輸出流out,緩衝區數組長度默認8192 */ public BufferedOutputStream(OutputStream out) { this(out, 8192); } /** * 構造函數,指定底層字節輸出流out,建立指定長度爲size的緩衝區數組 */ public BufferedOutputStream(OutputStream out, int size) { super(out); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; }
BufferedOutputStream的構造函數主要作了兩件事:綁定一個底層字節輸出流(真正負責寫入的對象)和建立內部緩衝區緩存
public synchronized void write(int b) throws IOException { if (count >= buf.length) { flushBuffer(); } buf[count++] = (byte)b; } private void flushBuffer() throws IOException { if (count > 0) { out.write(buf, 0, count); count = 0; } }
write(int b)方法寫入單個字節數據。首先判斷當前緩衝區是否寫滿若是已經寫滿,調用flushBuffer方法將緩衝區數據寫入到底層字節輸出流out中,重置緩衝區(count設置爲0重複利用緩衝區寫入),繼續寫入當前指定字節。數據結構
/** * 往流中寫入指定字節數組b的一部分(b下標off開始長度len) */ public synchronized void write(byte b[], int off, int len) throws IOException { //若寫入的字節數大於緩衝區,刷新當前緩衝區,把緩衝區數據寫入底層字節輸入流,後續寫入直接調用底層字節輸出流進行寫入 if (len >= buf.length) { flushBuffer(); out.write(b, off, len); return; } //若全部字節數據寫入後緩衝區會溢出那麼刷新當前緩衝區,把緩衝區數據寫入底層字節輸入流 if (len > buf.length - count) { flushBuffer(); } //將字節數據先寫入當前緩衝區 System.arraycopy(b, off, buf, count, len); //更新當前緩衝區有效字節個數count count += len; } /** * 刷新流,這個操做會強制將當前緩衝區中的字節數據寫入底層字節輸出流中 */ public synchronized void flush() throws IOException { flushBuffer(); out.flush(); }