BufferedOutputStream的快取功能解析(原始碼閱讀)
要介紹BufferedOutputStream,我們先了解一下OutputStream類
抽象類OutputStream類有三個write方法
- public abstract void write(int b)
- public void write(byte b[])
- public void write(byte b[], int off, int len)
由上面我們可以看出第一個write方法是讓子類覆蓋的,而第二個人write(byte b[])方法原始碼如下
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
所以可見最後處理還是呼叫第三個方法write(byte b[],int off,int len),該方法原始碼如下:
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
//注意這兒,這兒其實呼叫前面的抽象方法write(int b),同時還發生了自動轉型
write(b[off + i]);
}
}
問題
我們先不看抽象方法是如何實現的,也就是說OutputStream也具有快取器功能,我們可以將要寫入到流中的資料寫到一個byte[] buf陣列中,然後呼叫write(byte b[])或者write(byte b[], int off, int len)也可以,那為什麼還要BufferedInputStream類幹什麼呢,他們有什麼區別呢。同時我們知道BufferedInputStream類中還有一個flush()方法,在OutputStream流中沒有flush()方法,這又是為什麼呢?flush()是不是必須的呢,接下來看一下BufferedOutputStream類;
首先,BufferedOutput將OutputStream類物件作為一個構造方法的引數的。
首先看一下BufferedOutputStream 類原始碼
public
class BufferedOutputStream extends FilterOutputStream {
//這兒定義了一個byte[]陣列,用來充當快取器
protected byte buf[];
//這個變數是重點,他就是用來記錄當前快取器中的位元組數量的
protected int count;
//我們初始化建立一個物件的時候給了這個buf這個陣列8192個位元組.
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
// 這兒建立一個給定大小的陣列物件來充當快取器
buf = new byte[size];
}
public synchronized void write(int b) throws IOException {
if (count >= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
}
//該方法是重點
public synchronized void write(byte b[], int off, int len) throws IOException {
//如果傳進來的陣列長度大於buf 陣列的長度,則直接呼叫OutputStream物件的write方法。
if (len >= buf.length) {
flushBuffer();
out.write(b, off, len);
return;
}
//驗證BufferedOutputStream 類中buf剩下的空間能否裝得下傳進來的陣列。如果不能則先將當前buf陣列中資料寫入底層io流中
if (len > buf.length - count) {
flushBuffer();
}
//該處是重點,如果在當前BufferedOutputStream 類中buf陣列沒有滿,則將傳進來的陣列複製到當前類物件buf陣列中,同時更新count的值。
System.arraycopy(b, off, buf, count, len);
count += len;
//呼叫flushBuffer方法也就是將不滿8192個位元組陣列中的資料傳送出去。同時將count置零。
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
}
//強制將buf資料中未滿8192個位元組的資料寫入底層io中。
public synchronized void flush() throws IOException {
flushBuffer();
out.flush();
}
}
結論:
OutputStream的快取器(陣列)與BufferedOutputStream中類的快取器(陣列)本質是一樣的,只是BufferedOutputStream類中將要寫入到底層io流中的資料先湊個整,然後再一起寫入底層io流中,這樣就大大節省了io操作,大大提高了io利用率,寫一次io是很費資源的。這樣也出現了一個問題,假設向硬碟中寫入一個檔案,檔案最後資料比預設值8192個位元組小,則BufferOutputStream就不會將這些資料寫入底層io流中,造成檔案缺失,因此就需要在close()前呼叫flush()方法,強制將還沒有裝滿buf陣列的資料寫入底層io中。同時也可以看出節點流是不用flush()方法的,而一般的處理流都會採用固定buf這種方式的,比如常用的PrintWriter裡面其實操作的就是一個BufferedWriter物件,因此也需要呼叫flush()方法來重新整理,因為預設是不重新整理的。
相關文章
- Laravel 原始碼閱讀指南 -- Cookie 原始碼解析Laravel原始碼Cookie
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- Okhttp原始碼閱讀(二)——一個快取是怎麼觸發的HTTP原始碼快取
- myBatis原始碼解析-快取篇(2)MyBatis原始碼快取
- Vuex 原始碼解析(如何閱讀原始碼實踐篇)Vue原始碼
- Spring原始碼閱讀-IoC容器解析Spring原始碼
- .Net Core快取元件(MemoryCache)原始碼解析快取元件原始碼
- 【MyBatis原始碼解析】MyBatis一二級快取MyBatis原始碼快取
- ThinkPHP6 原始碼閱讀(六):Url 解析PHP原始碼
- java8LinkedList原始碼閱讀解析Java原始碼
- Spring 原始碼閱讀之標籤解析Spring原始碼
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- Volley 原始碼解析之快取機制原始碼快取
- myBatis原始碼解析-二級快取的實現方式MyBatis原始碼快取
- Laravel 原始碼閱讀指南 -- 載入和讀取 ENV 配置Laravel原始碼
- ReactorKit原始碼閱讀React原始碼
- AQS原始碼閱讀AQS原始碼
- CountDownLatch原始碼閱讀CountDownLatch原始碼
- HashMap 原始碼閱讀HashMap原始碼
- delta原始碼閱讀原始碼
- 原始碼閱讀-HashMap原始碼HashMap
- NGINX原始碼閱讀Nginx原始碼
- Mux 原始碼閱讀UX原始碼
- HashMap原始碼閱讀HashMap原始碼
- fuzz原始碼閱讀原始碼
- RunLoop 原始碼閱讀OOP原始碼
- express 原始碼閱讀Express原始碼
- muduo原始碼閱讀原始碼
- stack原始碼閱讀原始碼
- Masa Framework原始碼解讀-02快取模組(分散式快取進階之多級快取)Framework原始碼快取分散式
- 閱讀原始碼的樂趣原始碼
- Android記憶體快取LruCache原始碼解析Android記憶體快取原始碼
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)原始碼IDE
- ThinkPHP6 原始碼閱讀(五):多應用解析PHP原始碼
- 完全解析!Bert & Transformer 閱讀理解原始碼詳解ORM原始碼
- TiCDC 原始碼閱讀(四)TiCDC Scheduler 工作原理解析原始碼
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼