赞
踩
Java自学日记之IO流(一):字节流和字符流
Java自学日记之IO流(二):转换流(InputStreamReader、OutputStreamWriter)
前文讲了转换流,建立了字节流和字符流之间的沟通,接下来我们要考虑的就是如何更高效地读入写入文件,这里就引入了缓冲流
将读入输出流套上缓冲流之后,缓冲流会把数据存进一个大小为 capacity = 8192 = 8KB 的内部空间中
private static int DEFAULT_BUFFER_SIZE = 8192;
当空间不够时,
BufferedInputStream会重新对缓冲区填充数据,
BufferedOutputStream会调用flushBuffer(), 把缓冲区的数据写入对应的outputStream中, 然后将缓冲区清空重新读入
本质上都是提供了一个数据缓存的功能,这样最大的好处是减少了磁盘的操作次数。原始的InputStream类实现的read是即时读取的,即每一次读取都会是一次磁盘IO操作(哪怕只读取了1个字节的数据),而通过缓冲区的实现,读取可以读取缓冲区中的内容,当读取超过缓冲区的内容后再进行一次磁盘IO,载入一段数据填充缓冲,大幅减少了减少了磁盘IO的频率。下面聊具体实现
BufferedInputStream,BufferedOutputStream分别有两种构造方式,都是比较简单的套接一个原始的读取输入流(InputStream、OutputStream)
public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size)
public BufferedInputStream(OutputStream out)
public BufferedInputStream(OutputStream out, int size)
单参数的构造会在内部调用双参数的构造
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
双参数的构造会调用其父类(FilterInputStream、FilterOutputStream)的构造
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
父类
public FilterOutputStream(OutputStream out) {
this.out = out;
}
所以最后构造函数会把原始流存储在FilterInputStream/FilterOutputStream中
read经典两个构造函数
public synchronized int read() throws IOException
public synchronized int read(byte b[], int off, int len)
throws IOException
一个直接调用内部的buf数组返回一个字节(这里就能看出和原始流的区别了,原始流直接从磁盘文件读取一个字符,而BufferedInputStream则从内部缓冲区读取)
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
getBufIfOpen()返回buf数组,所以这个read函数返回 buf[pos] & 0xff
至于为什么 & 0xff ,感兴趣的可以去看看这篇博客
byte为什么要与上0xff?
另外一个read是给个数组,要求读取len个字节,除非读到了流的末尾,否则不断调用内部类read1读取
public synchronized int read(byte b[], int off, int len) throws IOException { getBufIfOpen(); // Check for closed stream if ((off | len | (off + len) | (b.length - (off + len))) < 0) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int n = 0; for (;;) { int nread = read1(b, off + n, len - n); if (nread <= 0) return (n == 0) ? nread : n; n += nread; if (n >= len) return n; // if not closed but no bytes available, return InputStream input = in; if (input != null && input.available() <= 0) return n; } }
不难看出,当read1(b, off + n, len - n)返回值<=0 或者 读取长度满足len 或者 no bytes available 时,可以返回,后两个都好理解,read1什么情况下会返回小于等于零呢,继续跟进
/** - Read characters into a portion of an array, reading from the underlying - stream at most once if necessary. */ private int read1(byte[] b, int off, int len) throws IOException { int avail = count - pos; if (avail <= 0) { /* If the requested length is at least as large as the buffer, and if there is no mark/reset activity, do not bother to copy the bytes into the local buffer. In this way buffered streams will cascade harmlessly. */ if (len >= getBufIfOpen().length && markpos < 0) { return getInIfOpen().read(b, off, len); } //当无数据可读时,从原始流中载入数据 fill(); avail = count - pos; if (avail <= 0) return -1; } int cnt = (avail < len) ? avail : len; System.arraycopy(getBufIfOpen(), pos, b, off, cnt); pos += cnt; return cnt; }
当读取的长度大于缓冲区的长度时:
这里要注意的是,这里的write是先往缓冲区里写,如果缓冲区满了,再输出
有两个write函数
public synchronized void write(int b) throws IOException
public synchronized void write(byte b[], int off, int len)
先看第一个
public synchronized void write(int b) throws IOException {
if (count >= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
}
当buf中有效字节数>=buf长度时(其实应该就是等于),flushBuffer()
flushBuffer () 这个方法干了什么呢, 继续跟进
/** Flush the internal buffer */
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
}
调用了一个write方法,输出buf里全部内容,并将count归零
接下来看write第二种构造
public synchronized void write(byte b[], int off, int len) throws IOException {
if (len >= buf.length) {
/* If the request length exceeds the size of the output buffer,
flush the output buffer and then write the data directly.
In this way buffered streams will cascade harmlessly. */
flushBuffer();
out.write(b, off, len);
return;
}
if (len > buf.length - count) {
flushBuffer();
}
System.arraycopy(b, off, buf, count, len);
count += len;
}
嗯,缓冲流差不多就这些了,明天会写字节数组流,并将它与缓冲流做一个对比
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。