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();
}
}
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)