nputStream 是所有表示字节输入流类的父类。windows操作系统的JDK8版本中,所有的InputStream的子类如下(此处只展示部分):
常用的InputStream子类有以下5个:
①、FileInputStream:处理文件的输入流。
②、ByteArrayInputStream :处理内存中byte[]数组的流式转换。
③、BufferedInputStream :带缓冲区的字节数组输入流,一般配合FileInputStream一起使用(缓冲区可以减少IO次数)。
④、ObjectInputStream:从流中读入一个自定义的对象。需要与ObjectOutputStream与配合使用,且按同样的顺序(写入的对象的顺序决定了读取对象的顺序)。
⑤、StringBufferInputStream:处理内存中String对象的流式转换。
以上5个子类的使用方式请参照:我的另一篇博客1、Java的IO概览(一)
InputStream.class的源码如下:
package java.io; public abstract class InputStream implements Closeable { // skip()函数中可以使用的最大缓冲区大小 private static final int MAX_SKIP_BUFFER_SIZE = 2048; //留给子类实现,子类必须遵守以下规则: //1、从输入的Stream中读取输入数据的下一个字节(ASCII码值),在读取到数据或者抛出异常前,这个函数是阻塞的。 //2、返回一个0 ~ 255 的 ASCII码值 。如果因为已经读到了Stream末尾而没有可用的字节,则返回值 -1。 public abstract int read() throws IOException; //将从输入的Stream中读取的ASCII码值放入到byte[]数组中,0表示从byte[]数组的第0个索引开始,b.length表示一次性向byte[]数组中放入的ASCII码值的数量 //在读取到数据或者抛出异常前,这个函数是阻塞的。 public int read(byte b[]) throws IOException { return read(b, 0, b.length); } //将从输入的Stream中读取的字节(ASCII码值)放入到byte[]数组中,off表示从byte[]数组的第off个索引开始,len表示一次性向byte[]数组中放入的字节(ASCII码值)的数量 //在读取到数据或者抛出异常前,这个函数是阻塞的。 public int read(byte b[], int off, int len) throws IOException { if (b == null) {//byte[]数组不能为空 throw new NullPointerException(); //范围检测,off和len必须是非负数,b.length - off是byte[]数组还可以放的字节(ASCII码值)的数量 } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException();//范围检查失败,抛出一个IndexOutOfBoundsException异常 } else if (len == 0) {//len=0,则直接返回0,该Stream可能有可读的字节(ASCII码值),也可能没有可读的字节(ASCII码值),但是本次不读任何数据。 return 0; } int c = read();//最终还是调用子类实现的read()函数 if (c == -1) {//read()函数规定了,返回值-1表示已经读到了Stream末尾而没有可用的字节(ASCII码值) return -1;//如果一开始就读到了Stream末尾而没有可用的字节(ASCII码值),则直接返回-1 } b[off] = (byte)c;//如果一开始从Stream中可以读到字节(ASCII码值),则将读到的第1个字节(ASCII码值)值放入byte[]数组的第off个索引位置 //如果从Stream中读到了第1个字节(ASCII码值),则接着从Stream中读后面的字节(ASCII码值) int i = 1; try { for (; i < len ; i++) { c = read();//最终还是调用子类实现的read()函数 if (c == -1) { break;//读不到,结束循环 } b[off + i] = (byte)c;//每次从Stream中读到的字节(ASCII码值)都放到byte[]数组的第off个索引位置之后 } } catch (IOException ee) { } return i;//返回从Stream中读到的字节数量 } //将从输入的Stream中跳过n个字节 public long skip(long n) throws IOException { //还没(或者还需要)跳过的字节(ASCII码值)的总数量 long remaining = n; int nr; //校验,如果n<=0,则返回0 if (n <= 0) { return 0; } //每次跳过的字节(ASCII码值)最多为2048个 int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); //用于每次跳过指定数量字节(每次最多为2048个)的数组 byte[] skipBuffer = new byte[size]; while (remaining > 0) {//还需要跳过的字节(ASCII码值)数量<=0时,跳出循环 //调用read(byte b[], int off, int len)函数,该函数在上面已经分析过,该函数的返回值有3种,含义如下: //①、返回值=-1,表示该Stream没有可读的字节 //②、返回值=0,表示该Stream可能有可读的字节(ASCII码值),也可能没有可读的字节(ASCII码值),但是本次传入的Math.min(size, remaining)为0,不从Stream中读任何数据,此处的Math.min(size, remaining)不可能为0 //③、返回值>0,表示从该Stream中读到的字节(ASCII码值)的数量 nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); if (nr < 0) { break;//该Stream中没有可读的字节时,nr=-1,跳出循环 } remaining -= nr;//表示还需要跳过的字节(ASCII码值)数量 } return n - remaining;//返回已经跳过的字节(ASCII码值)的总数量 } //返回这个Stream中还可以读取的字节的总数量,JDK不建议将这个函数的返回值作为缓冲区的长度来从Stream中读取数据(子类一般会覆盖这个函数) public int available() throws IOException { return 0; } //留给子类实现,子类必须遵守以下规则: //关闭Stream,并释放与该流相关的系统资源 public void close() throws IOException {} //标记此Stream中的当前位置。随后调用reset()函数会将此流重新定位到上次标记的位置,从而使得后续的读取操作能够再次读取相同的字节。 //带有回退功能的InputStream的子类会重写这个函数,但是FileInputStream不会重写这个函数,也就意味着,FileInputStream不支持回退功能 public synchronized void mark(int readlimit) {} //reset()函数会将此流重新定位到上次标记的位置,从而使得后续的读取操作能够再次读取相同的字节。 //带有回退功能的InputStream的子类会重写这个函数,但是FileInputStream不会重写这个函数,也就意味着,FileInputStream不支持回退功能 public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); } //如果InputStream的子类支持mark()函数和 reset()函数,则返回true,否则,返回false(InputStream的子类不支持mark()函数和 reset()函数)。 public boolean markSupported() { return false; } }1.1、InputStream的skip()函数
public long skip(long n) throws IOException { //还没(或者还需要)跳过的字节(ASCII码值)的总数量 long remaining = n; int nr; //校验,如果n<=0,则返回0 if (n <= 0) { return 0; } //每次跳过的字节(ASCII码值)最多为2048个 int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); //用于每次跳过指定数量字节(每次最多为2048个)的数组 byte[] skipBuffer = new byte[size]; while (remaining > 0) {//还需要跳过的字节(ASCII码值)数量<=0时,跳出循环 //调用read(byte b[], int off, int len)函数,该函数在上面已经分析过,该函数的返回值有3种,含义如下: //①、返回值=-1,表示该Stream没有可读的字节 //②、返回值=0,表示该Stream可能有可读的字节(ASCII码值),也可能没有可读的字节(ASCII码值),但是本次传入的Math.min(size, remaining)为0,不从Stream中读任何数据,此处的Math.min(size, remaining)不可能为0 //③、返回值>0,表示从该Stream中读到的字节(ASCII码值)的数量 nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); if (nr < 0) { break;//该Stream中没有可读的字节时,nr=-1,跳出循环 } remaining -= nr;//表示还需要跳过的字节(ASCII码值)数量 } return n - remaining;//返回已经跳过的字节(ASCII码值)的总数量 }如果要从一个20000个字节的Stream中跳过6000个字节,只需要调用skip(6000)即可,该函数的执行过程分为以下4步:
①、while循环之前进行初始化byte[]数组和零时变量的操作
②、第1次while循环之后,已经从前Stream中读取了2048个字节
③、第2次while循环之后,已经从前Stream中读取了4096个字节
④、第3次while循环之后,已经从前Stream中读取了6000个字节,读取完毕,byte[]数组的前1094个位置是本次从Stream流中读取的第4097第6000个字节,byte[]数组的后954个位置仍然是上一次从Stream流中读取的第3143第4096个字节
二、FilterInputStream 源码——装饰器基类
FilterInputStream 的UML关系图,如下所示:
FilterInputStream.class的源码,如下所示:
package java.io; public class FilterInputStream extends InputStream { //用来组合了一个 被装饰者的变量,被修饰为volatile 有以下3个原因: // 1. 确保多线程环境下修改的可见性 // 2. 有些装饰器允许运行时替换底层流 // 3. 防止指令重排序导致的初始化问题 protected volatile InputStream in; //创建时传入一个 被装饰者 protected FilterInputStream(InputStream in) { this.in = in; } //调用被装饰者的read()函数 public int read() throws IOException { return in.read(); } //调用被装饰者的read(byte b[]) 函数 public int read(byte b[]) throws IOException { return read(b, 0, b.length); } //调用被装饰者的read(byte b[], int off, int len)函数 public int read(byte b[], int off, int len) throws IOException { return in.read(b, off, len); } //调用被装饰者的skip()函数 public long skip(long n) throws IOException { return in.skip(n); } //调用被装饰者的available()函数 public int available() throws IOException { return in.available(); } //调用被装饰者的close()函数 public void close() throws IOException { in.close(); } //调用被装饰者的mark()函数 public synchronized void mark(int readlimit) { in.mark(readlimit); } //调用被装饰者的reset()函数 public synchronized void reset() throws IOException { in.reset(); } //调用被装饰者的markSupported()函数 public boolean markSupported() { return in.markSupported(); } }