news 2026/6/30 2:01:12

InputStream 源码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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) {//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(); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/30 2:00:43

逆向工程实战:深度剖析易游网络验证的攻防策略与防护设计

1. 项目概述&#xff1a;易游网络验证的攻防世界易游网络验证&#xff0c;对于很多从事软件逆向分析的朋友来说&#xff0c;是一个既熟悉又充满挑战的名字。它广泛存在于各类共享软件、游戏辅助工具和商业应用中&#xff0c;作为一道坚固的防线&#xff0c;保护着开发者的核心利…

作者头像 李华
网站建设 2026/6/30 1:55:26

多次调用,顺序返回不同结果

gomonkey.ApplyMethodSeq()&#xff08;高频实用&#xff09;方法处理「结构体方法被多次调用&#xff0c;需要返回不同结果」的核心方法&#xff0c;完美解决&#xff1a;方法第 1 次调用返回 A、第 2 次返回 B、第 N 次返回默认值 / 报错 这类高频业务场景。这个也和上面&…

作者头像 李华
网站建设 2026/6/30 1:54:22

用过才敢说!2026年亲测好用的专业AI论文网站

2026年AI论文写作工具已从“基础生成”升级为多场景适配的智能研究助手&#xff0c;核心评价维度涵盖文献真实性、格式合规性、长文本逻辑、查重降重、AIGC合规与跨语言支持。本次测评覆盖6款主流工具&#xff0c;涵盖中文与英文、全流程与专项功能、免费与付费版本&#xff0c…

作者头像 李华
网站建设 2026/6/30 1:53:30

信号链路——从采样电阻到电流数值

核心问题&#xff1a;三相电流从电机相线流出来&#xff0c;经过采样电阻、运放、ADC&#xff0c;最后变成代码里的i_fb——这条路每一步做了什么&#xff1f;怎么推导换算系数&#xff1f;我的板子参数&#xff1a;Rshunt1mΩ,Gain50,V_bias1.5V,Vs3.3V1.为什么需要偏置电压先…

作者头像 李华