原文发布在 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() 方法,防止数据没法写到输出流中。
学习没有一蹴而就的方法,坚持思考、练习才是王道~