原文發佈在 JavaIO中神奇的flush,不少人閱讀過這篇,特此推薦給掘金的朋友們。java
Java IO流的設計不得不讓人拍案叫絕,佩服設計者鬼斧天工的手法。編程
若是你是Java初學者,我敢保證第一次接觸Java的IO類,必定會 」狂暈!」,倒不是由於它有多麼難學而是太多,並且及其讓人有種 「不識廬山真面目」 的感受,當你學完他以後又有種 「只緣身在此山中」 的頓悟。安全
在Java編程的日子中尤爲是在網絡編程中,幾乎離不開Java的IO流,因此學好他是頗有必要的。網絡
關於Java的IO流的分類,能夠到網上soso,今天跟你們分享一下神奇的 flush 方法。編程語言
該類實現了 Flushable 接口,因此重寫了 flush 方法,看看 flush() 源碼,會更加的讓你明白:學習
public void flush() throws IOException { }
複製代碼
sorry,該實現爲空。 這裏的 flush 竟然是一個空方法,什麼也沒作。看清楚啊,該方法不是抽象方法,是一個實實在在的方法。除了方法體中一無全部,其它還好!看JDK的API如何解釋吧!spa
flush public void flush() throws IOException 刷新此輸出流並強制寫出全部緩衝的輸出字節。 flush 的常規協定是:若是此輸出流的實現已經緩衝了之前寫入的任何字節,則調用此方法指示應將這些字節當即寫入它們預期的目標。 若是此流的預期目標是由基礎操做系統提供的一個抽象(如一個文件),則刷新此流只能保證將之前寫入到流的字節傳遞給操做系統進行寫入,但不保證能將這些字節實際寫入到物理設備(如磁盤驅動器)。 OutputStream 的 flush 方法不執行任何操做。 指定者: 接口 Flushable 中的 flush 拋出: IOException - 若是發生 I/O 錯誤。 複製代碼
開始,我安慰本身,該類是一個抽象類,它的子類確定重寫了該方法。操作系統
好吧,OutputStream 的直接子類有:ByteArrayOutputStream 、FileOutputStream、FilterOutputStream、ObjectOutputStream 、OutputStream、PipedOutputStream 等幾個類。設計
注意:這裏的子類 OutputStream 是包 org.omg.CORBA.portable 的。3d
對於 FileOutputStream、ByteArrayOutputStream、org.omg.CORBA.portable.OutputStream 類它們的flush() 方法均是從父類繼承的 flush 方法。
FilterOutputStream 類重寫了 flush() 方法,可是實質仍是調用父類的 flush() 方法。
ObjectOutputStream、PipedOutputStream 類重寫了 flush() 方法。
來舉兩個小例子,第一個例子主要是向文本中寫入字符串,第二個例子向文本中寫入必定字節的數據。
一、例子1:向文本中寫入字符串
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception {
File file = new File("text.txt");
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(fos);
dos.writeBytes("java io");
}
}
複製代碼
二、例子2:向文本中寫入必定字節的數據
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception {
File file = new File("text.txt");
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] b = new byte[1024*8];
bos.write(b);
bos.flush();
}
}
複製代碼
這兩段代執行後,分別會在當前目錄下產生7字節的文件(內容爲 java io)和1KB字節的文件。
說到這裏,有些人會說,這有什麼稀奇,至於嗎???呵呵,別急哈!
如今咱們修改第二個代碼,主要是註釋掉調用 flush() 方法,以下:
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception {
File file = new File("text.txt");
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] b = new byte[1024];
bos.write(b);
//bos.flush();
}
}
複製代碼
OK,再次運行代碼,額的神啊???文件大小竟然是0字節。
why?
仔細的你會發現,第一個例子中的代碼中並無調用 flush() 方法,竟然能夠正常的寫入。爲何第二個就不能夠呢?仍是從源碼入手找答案吧!
DataOutputStream 繼承 FilterOutputStream ,實現了 DataOutput 接口。咱們知道 FilterOutputStream 類重寫了 flush() 方法,可是實質仍是調用父類的 flush() 方法。DataOutputStream 類的 flush() 方法效仿其父類 FilterOutputStream 的作法,以下:
public void flush() throws IOException {
out.flush();
}
複製代碼
那麼,即便你在第一個例子的代碼後面加上 dos.flush() 結果也是正常的,與不加是同樣的效果,由於它們的父類 flush() 方法均爲空,這就是爲何第一個代碼的神奇所在。
再看看第二個代碼的 「病因」 在哪裏?先看看 BufferedOutputStream 類的結構:
public class BufferedOutputStream extends FilterOutputStream 複製代碼
再看看,它的 flush() 方法:
public synchronized void flush() throws IOException {
flushBuffer();
out.flush();
}
/** Flush the internal buffer */
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
}
複製代碼
不錯,該類重寫了 flush() 方法,不像前面幾個類那樣不是繼承就是山寨父類的 flush() 方法。BufferedOutputStream 類是一個使用了緩衝技術的類,這種類一把都會本身實現 flush() 方法。
那麼,有人會問使用這種類的時候,難道必須使用 flush() 方法嗎,固然不是嘍??!!不過有個前提,你的字節數據必須不能小於8KB。實例代碼,注意沒有 flush()方法。
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception {
File file = new File("text.txt");
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] b = new byte[1024*8];
bos.write(b);
//bos.flush();
}
}
複製代碼
執行代碼,會產生8KB的文本文件。
固然,怎麼可能每時每刻都知道你的數據必定會不小於8KB呢,因此調用 flush() 方法比較安全。
不過,話又說回來,通常用完IO流以後(若是你有一個好的習慣)咱們都會去調用 close() 方法,看源碼能夠知道該方法也是調用相對應的 flush() 方法。
這裏提醒一下,若是你的文件讀寫沒有達到預期目的,十之八九是由於你沒有調用 flush() 或者 close() 方法。
另外,字符流類大多數都實現了 flush() 或者 close() 方法,只不過,它們調用的是 StreamEncoder 類的該方法。該類位於 sun.nio.cs 包下面,其源碼在咱們JDK中是沒有的。
能夠點擊 StreamEncoder.java 查看源碼。
該類 Writer 是一個抽象類,聲明以下:
public abstract class Writer implements Appendable, Closeable, Flushable 複製代碼
Writer 類的 flush() 方法是一個抽象方法,其子類通常都實現了該方法。
因此,通常使用字符流以後須要調用一下 flush() 或者 close() 方法。
abstract public void flush() throws IOException;
複製代碼
細節請看JDK的API,或者Java的源碼以及上面的 StreamEncoder 類源碼。
今天就說到這裏吧,本文主要藉助Java IO中字節流與字符流的 flush() 方法,來講明學編程語言看源碼和思考是很重要的。
總之,無論你使用哪一種流(字符、字節、具備緩衝的流)技術,不妨調用一下 flush() 或者 close() 方法,防止數據沒法寫到輸出流中。
學習沒有一蹴而就的方法,堅持思考、練習纔是王道~