前言
月是一轮明镜,晶莹剔透,代表着一张白纸(啥也不懂)
央是一片海洋,海乃百川,代表着一块海绵(吸纳万物)
泽是一柄利剑,千锤百炼,代表着千百锤炼(输入输出)
月央泽,学习的一种过程,从白纸->吸收各种知识->不断输入输出变成自己的内容
希望大家一起坚持这个过程,也同样希望大家最终都能从零到零,把知识从薄变厚,再由厚变薄!
一.BufferedInputStream的作用:
直接看源码注释(我的翻译可能不太准,如果道友们有更棒的理解,可以留言或者私信)
/**
* A <code>BufferedInputStream</code> adds
* functionality to another input stream-namely,
* the ability to buffer the input and to
* support the <code>mark</code> and <code>reset</code>
* methods. When the <code>BufferedInputStream</code>
* is created, an internal buffer array is
* created. As bytes from the stream are read
* or skipped, the internal buffer is refilled
* as necessary from the contained input stream,
* many bytes at a time. The <code>mark</code>
* operation remembers a point in the input
* stream and the <code>reset</code> operation
* causes all the bytes read since the most
* recent <code>mark</code> operation to be
* reread before new bytes are taken from
* the contained input stream.
* BufferedInputStream向另一个输入流添加功能,即缓冲输入和支持mark和reset方法的能力。
* 创建BufferedInputStream时,会创建一个内部缓冲区数组。当读取或跳过流中的字节时,
* 内部缓冲区会根据需要从包含的输入流中重新填充,一次很多字节。mark操作记住输入流中的一个点,
* reset操作导致在新字节被读取之前重新读取自最近的mark操作以来读取的所有字节取自包含的输入流。
* @author Arthur van Hoff
* @since JDK1.0
*/
二.类图:
三.成员变量:
//默认缓冲区大小
private static int DEFAULT_BUFFER_SIZE = 8192;
/**
* 要分配的数组的最大大小。一些 VM 在数组中保留一些头字。
* 尝试分配更大的数组可能会导致 OutOfMemoryError:请求的数组大小超出 VM 限制
*/
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
/**
* 存储数据的内部缓冲区数组。必要时,它可能会被另一个不同大小的数组替换
*/
protected volatile byte buf[];
/**
* 原子更新程序为 buf 提供 compareAndSet。这是必要的,因为关闭可以是异步的。
* 我们使用 buf[] 的空值作为该流已关闭的主要指示符。 (“in”字段在关闭时也会被清零。)
*/
private static final
AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
AtomicReferenceFieldUpdater.newUpdater
(BufferedInputStream.class, byte[].class, "buf");
/**
* 比缓冲区中最后一个有效字节的索引大 1 的索引。该值始终在0到buf.length范围内;
* 元素buf[0]到buf[count-1]包含从底层输入流获得的缓冲输入数据。
*/
protected int count;
/**
* 1.缓冲区中的当前位置。这是要从buf数组中读取的下一个字符的索引
* 2.该值始终在0到count的范围内。如果小于count,则buf[pos]是下一个要作为输入提供的字节;
* 如果它等于count,则下一个read或skip操作将需要从包含的输入流中读取更多字节
*/
protected int pos;
/**
* 1.在最后一个mark方法被调用时pos字段的值
* 2.该值始终在-1到pos的范围内。如果输入流中没有标记位置,则该字段为-1。
* 如果输入流中有标记位置,则buf[markpos]是reset操作后要作为输入提供的第一个字节。
* 如果markpos不是-1,则从位置buf[markpos]到buf[pos-1]的所有字节必须保留在缓冲区数组
* (尽管它们可能会移动到缓冲区数组中的另一个位置,并对count、pos和markpos的值进行适当调整);
* 除非pos和markpos之间的差异超过marklimit,否则它们可能不会被丢弃
*/
protected int markpos = -1;
/**
* 在调用mark方法之后,在后续调用reset方法失败之前允许的最大预读。
* 每当pos和markpos之间的差异超过marklimit时,可以通过将markpos设置为-1<来删除标记代码
*/
protected int marklimit;
四.构造方法:
/**
* 创建一个BufferedInputStream并保存它的参数,输入流in,供以后使用。
* 一个内部缓冲区数组被创建并存储在buf中。
*/
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
/**
* 使用指定的缓冲区大小创建一个BufferedInputStream,并保存它的参数,
* 输入流in,供以后使用。长度为size的内部缓冲区数组被创建并存储在buf中。
*/
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
五.内部方法:
fill
/**
* 用更多数据填充缓冲区,考虑到处理标记的混洗和其他技巧。假设它正在被同步方法调用。
* 此方法还假设所有数据都已读入,因此 pos > count。
*/
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer 无标记:丢弃缓冲区*/
else if (pos >= buffer.length) /* no room left in buffer 缓冲区中没有剩余空间*/
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
} else if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents
缓冲区太大,使标记 pos = 0 无效;删除缓冲区内容
*/
} else if (buffer.length >= MAX_BUFFER_SIZE) {
throw new OutOfMemoryError("Required array size too large");
} else { /* grow buffer */
int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
pos * 2 : MAX_BUFFER_SIZE;
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos);
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
//如果有异步关闭,则无法替换 buf。注意:如果 fill() 可被多个线程访问,
// 则需要更改此设置。但就目前而言,CAS 失败的唯一方法是通过 close。断言 buf == null
throw new IOException("Stream closed");
}
buffer = nbuf;
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
read
/**
* 参见InputStream的read方法的通用约定。
*/
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
/**
* 1.从此字节输入流中读取字节到指定的字节数组中,从给定的偏移量开始。
* 2.该方法实现了InputStream类对应的InputStream.read(byte[], int, int) read方法的通用约定。
* 作为额外的便利,它尝试通过重复调用底层流的read方法来读取尽可能多的字节。
* 3.这个迭代的read一直持续到以下条件之一变为真:
* 1)已读取指定数量的字节,
* 2)底层流的read方法返回-1,表示文件结束,或者底层流的available方法返回零,
* 表示进一步的输入请求将被阻止
* 3)如果底层流上的第一个read返回-1以指示文件结束,则此方法返回-1。否则此方法返回实际读取的字节数。
* 4.鼓励但不要求此类的子类尝试以相同的方式读取尽可能多的字节。
*/
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;
}
}
skip
/**
* 参见InputStream的skip方法的通用约定
*/
public synchronized long skip(long n) throws IOException {
getBufIfOpen(); // Check for closed stream
if (n <= 0) {
return 0;
}
long avail = count - pos;
if (avail <= 0) {
// If no mark position set then don't keep in buffer
//如果没有设置标记位置,则不要保留在缓冲区中
if (markpos <0)
return getInIfOpen().skip(n);
// Fill in buffer to save bytes for reset
//填充缓冲区以保存用于重置的字节
fill();
avail = count - pos;
if (avail <= 0)
return 0;
}
long skipped = (avail < n) ? avail : n;
pos += skipped;
return skipped;
}
available
/**
* 1.返回可以从此输入流读取(或跳过)的字节数的估计值,而不会因下一次调用此输入流的方法而阻塞。
* 下一次调用可能是同一个线程或另一个线程。单次读取或跳过这么多字节不会阻塞,但可能读取或跳过更少的字节
* 2.此方法返回缓冲区中剩余要读取的字节数 (count - pos) 和调用java.io.FilterInputStream.in
* .available() 的结果之和。
*/
public synchronized int available() throws IOException {
int n = count - pos;
int avail = getInIfOpen().available();
return n > (Integer.MAX_VALUE - avail)
? Integer.MAX_VALUE
: n + avail;
}
mark
/**
* 参见InputStream<code>的mark方法的通用约定
*/
public synchronized void mark(int readlimit) {
marklimit = readlimit;
markpos = pos;
}
reset
/**
* 1.参见InputStream的reset方法的通用约定
* 2.如果markpos是-1(未设置标记或标记已失效),则抛出IOException。
* 否则,pos设置为等于markpos
*/
public synchronized void reset() throws IOException {
getBufIfOpen(); // Cause exception if closed
if (markpos < 0)
throw new IOException("Resetting to invalid mark");
pos = markpos;
}
markSupported
/**
* 测试此输入流是否支持mark和reset方法。BufferedInputStream的markSupported方法返回true。
*/
public boolean markSupported() {
return true;
}
close
/**
* 关闭此输入流并释放与该流关联的所有系统资源。一旦流关闭,
* 进一步的 read()、available()、reset() 或 skip()
* 调用将抛出 IOException。关闭先前关闭的流没有任何效果
*/
public void close() throws IOException {
byte[] buffer;
while ( (buffer = buf) != null) {
if (bufUpdater.compareAndSet(this, buffer, null)) {
InputStream input = in;
in = null;
if (input != null)
input.close();
return;
}
// Else retry in case a new buf was CASed in fill()
//否则重试以防在 fill() 中 CASed 新的 buf
}
}
六.总结:
终于来到io包,不容易....