InputStream 源码

发布时间:2026/6/30 2:01:19
InputStream 源码 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) {//len0则直接返回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; //校验如果n0则返回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否则返回falseInputStream的子类不支持mark()函数和 reset()函数。 public boolean markSupported() { return false; } }1.1、InputStream的skip()函数public long skip(long n) throws IOException { //还没(或者还需要)跳过的字节ASCII码值的总数量 long remaining n; int nr; //校验如果n0则返回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(); } }