Java中文文档

image-20191124152312465

  1. 来看一下FileOutputStream

    • FileOutputStream.flush()

      image-20191124153409164

      进入后发现是直接调用其父类也就是OutputStream里面的flush()方法。也就是FileOutputStream并没有对这个方法重写。嗯~ 没啥问题。

    • FileOutputStream.close()

      image-20191124152544659

      进去查看close的具体实现,发现FileOutputStream重写了基类的close()方法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22

      public void close() throws IOException {
      synchronized (closeLock) { //上个锁
      if (closed) {
      return;
      }
      closed = true;
      }

      // Android-added: CloseGuard support.
      guard.close(); // 记录防止资源泄漏

      if (channel != null) { // 如果有channel一起关闭
      channel.close();
      }

      // BEGIN Android-changed: Close handling / notification of blocked threads.
      if (isFdOwner) {
      IoBridge.closeAndSignalBlockedThreads(fd);
      }
      // END Android-changed: Close handling / notification of blocked threads.
      }

      可以看到在关闭文件流前做了一些资源泄露的检查等工作,但并没有调用flush()来刷新流,所以如果此时流中还有数据,并且没有手动刷新,直接关闭数据可能会丢失。

  2. 再来看一下BufferedOutputStream:

    BufferedOutputStream继承关系:

    image-20191124162955804

    • BufferedOutputStream.flush():
1
2
3
4
public synchronized void flush() throws IOException {
flushBuffer();
out.flush();
}
1
2
3
4
5
6
7
   /** 刷新内部缓冲区 */
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
}

BufferedOutputStream提供了缓冲区的功能,所以在flush方法里增加了缓冲区刷新的功能

  • BufferedOutputStream.close():

可以发现BufferedOutputStream() 的父类FilterOutputStream()对基类的方法进行了重写,在close()前还调用了一次flush()方法

1
2
3
4
5
6
7
   
@SuppressWarnings("try")
public void close() throws IOException {
try (OutputStream ostream = out) {
flush();
}
}

这里的flush()方法并不是基类的flush,FilterOutputStream()也对这个函数进行了重写:

1
2
3
4
   
public void flush() throws IOException {
out.flush();
}

上面的out变量即为基类的一个对象,用来调用基类的方法。

1
protected OutputStream out;

综合起来看发现,FilterOutputStream()方法是对close方法进行重写,在关闭流之前自动调用了一次刷新,这样即使缓冲区还有数据,我们直接调用BufferedOutputStream的close方法也不会造成数据的丢失。

BufferedOutputStream全部源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public
class BufferedOutputStream extends FilterOutputStream {
/**
* 存储数据的内部缓冲区
*/
protected byte buf[];

/**
缓冲区中的有效字节数。
*/
protected int count;

/**
* 创建新的缓冲输出流以将数据写入指定的基础输出流
*/
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];
}

/** 刷新内部缓冲区 */
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
}

/**
* 将指定的字节写入此缓冲输出流
*/
public synchronized void write(int b) throws IOException {
if (count >= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
}

/**
* 将从偏移量off开始的指定字节数组的len长度字节写入缓冲输出流。通常,此方法将给定数组中的字节存储到此流的缓冲区中,根据需要将缓冲区刷新到基础输出流。但是,如果请求的长度至少与此流的缓冲区一样大,则此方法将刷新缓冲区并将字节直接写入基础输出流。因此,冗余的BufferedOutputStream不会不必要地复制数据。
*/
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 += len;
}

/**
* 刷新此缓冲输出流。这将强制将任何缓冲输出字节写入底层输出流。
*/
public synchronized void flush() throws IOException {
flushBuffer();
out.flush();
}
}