版本
JDK8(JDK1.8)
InputStream虚拟类源码重点
1.InputStream虚拟类实现 Closeable 接口,自然就具有 Closeable 接口特性
Closeable 源码可以看我这篇文章 Closeable
2.InputStream虚拟类只定义了一个虚拟方法abstract int read(),用于阻塞地读取一个字节,而该虚拟类的其他部分方法都使用该虚拟方法来实现,所以其子类只需要实现一个read()方法即可
3.InputStream虚拟类方法
| 方法名 | 作用 |
|---|---|
| abstract int read() | 阻塞地读取一个字节,返回0到255范围内的整数 |
| int read(byte b[]) | 有限循环阻塞地读取字节填满字节数组b,返回实际读取的字节数(遇到文件结尾或出错就无法填满,下面也一样) |
| int read(byte b[], int off, int len) | 有限循环阻塞地读取字节从字节数组b偏移量off处开始存放,最多读取len个字节,返回实际读取的字节数 |
| byte[] readAllBytes() | 读取输入流所有字节存到字节数组中,最多读取2G的文件(由于字节数组长度有限) |
| int readNBytes(byte[] b, int off, int len) | 无限循环阻塞地读取字节从字节数组b偏移量off处开始存放,正常情况读取len个字节才返回,返回实际读取的字节数 |
| long skip(long n) | 跳过并丢弃此输入流中的n字节数据,使用read()实现,效率较低 |
| int available() | 返回可从此输入流读取(或跳过)的字节数的估计值 |
| void close() | 关闭此输入流并释放与该流关联的所有系统资源 |
| synchronized void mark(int readlimit) | 标记此输入流中的当前位置 |
| synchronized void reset() | 将此流重新定位到上次对此输入流调用mark方法时的位置 |
| boolean markSupported() | 测试此输入流是否支持标记mark和重置reset方法 |
| long transferTo(OutputStream out) | 从该输入流使用read()读取所有字节,并按读取顺序将字节写入给定的输出流 |
long skip(long n) 原生实现的是RandomAccessFile类
RandomAccessFile源码可以看我这篇文章 RandomAccessFile
InputStream虚拟类源码
package java.io;
import java.util.Arrays;
import java.util.Objects;
public abstract class InputStream implements Closeable {
// MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to
// use when skipping.
// 最大跳过缓冲区大小用于确定跳过时要使用的最大缓冲区大小。
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
// 默认缓存区大小
private static final int DEFAULT_BUFFER_SIZE = 8192;
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
// 尝试读取一个字节
int c = read();
// 如果到了文件的结尾
if (c == -1) {
// 直接返回-1
return -1;
}
// 如果读取到该字节,设置到字节数组b中
b[off] = (byte)c;
// 循环len-1次
int i = 1;
try {
for (; i < len ; i++) {
// 每次读取一个字节
c = read();
// 如果遇到文件结尾跳出循环
if (c == -1) {
break;
}
// 如果该字节成功读取,将其设置到字节数组中
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
// 读取所有字节到字节数组中,除非文件大于2G,不然可以容纳下
public byte[] readAllBytes() throws IOException {
// 创建一个缓冲池字节数组 8KB大小
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
// 容量为缓冲字节数组长度
int capacity = buf.length;
int nread = 0;
int n;
for (;;) {
// read to EOF which may read more or less than initial buffer size
// 读取到EOF,该EOF可以读取大于或小于初始缓冲区大小
// EOF即结尾
// 循环进行这个操作,直到把缓冲字节数组填满
while ((n = read(buf, nread, capacity - nread)) > 0)
//由于n不确定,每次读取的字节也不确定
//根据读取的字节确定偏移量nread和
//现在要读取的剩余字节长度capacity - nread
nread += n;
// if the last call to read returned -1, then we're done
// 如果最后一个read调用返回-1,那么我们就完成了
if (n < 0)
break;
// need to allocate a larger buffer
// 需要分配更大的缓冲区
// 如果上面缓存区填满了,仍然有数据没有读取完,就进行缓存区扩容操作
// 如果容量小于 最大缓存区容量 - 容量 ,即 capacity <= MAX_BUFFER_SIZE / 2
// 即小于最大容量的二分之一
if (capacity <= MAX_BUFFER_SIZE - capacity) {
// 进行二倍扩容
capacity = capacity << 1;
} else {
// 如果扩容前容量已经等于最大缓冲池大小,则抛出异常
if (capacity == MAX_BUFFER_SIZE)
throw new OutOfMemoryError("Required array size too large");
// 将容量设为最大缓冲池大小
capacity = MAX_BUFFER_SIZE;
}
// 复制数组进行扩容
buf = Arrays.copyOf(buf, capacity);
}
// 如果容量等于偏移量,说明字节数组已经被填满,可以直接返回
// 如果容量大于偏移量,说明字节数组有空闲空间,需要复制缩容
return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
}
public int readNBytes(byte[] b, int off, int len) throws IOException {
// 如果b为空,抛出异常
Objects.requireNonNull(b);
// 如果索引越界,抛出异常
if (off < 0 || len < 0 || len > b.length - off)
throw new IndexOutOfBoundsException();
// n为读取的字节总数
int n = 0;
// 如果读取字节数小于要求的读取字节数,则循环不断读取
while (n < len) {
// count返回实际读取字节数
// 每次读取的偏移量 off + n 和要读取的字节数 len - n 都得重新设置
int count = read(b, off + n, len - n);
// 如果遇到文件结尾,则退出循环
if (count < 0)
break;
// 更新读取字节总数
n += count;
}
return n;
}
public long skip(long n) throws IOException {
// 剩余要跳过的字节 remaining
long remaining = n;
int nr;
// 如果要跳过的字节小于n
if (n <= 0) {
return 0;
}
// 如果要跳过的字节数大于2KB,则取2KB
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
// 跳过的字节缓冲数组
byte[] skipBuffer = new byte[size];
// 当要跳过的剩余字节大于0
while (remaining > 0) {
// 使用read(.) 方法来达到跳过字节目的,效率比较低
// nr为本次read实际读取到的字节数,也就是实际跳过的字节数
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
// 更新要跳过的剩余字节数
remaining -= nr;
}
// 返回实际跳过的字节数
// 即期望跳过的字节数 - 剩余要跳过的字节数
return n - remaining;
}
public int available() throws IOException {
return 0;
}
public void close() throws IOException {}
public synchronized void mark(int readlimit) {}
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
public boolean markSupported() {
return false;
}
public long transferTo(OutputStream out) throws IOException {
// 如果out为空,则抛出异常
Objects.requireNonNull(out, "out");
// 总共传输给输出流的字节
long transferred = 0;
// 创建缓冲数组,默认大小为8KB
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
// 每次读取的字节数
int read;
// 如果每次都读取到了字节,并放置在缓冲数组中
while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
// 则将该缓冲数组中有效数据,写入到输出流中
out.write(buffer, 0, read);
// 更新总共传输给输出流的字节数
transferred += read;
}
// 返回总共传输给输出流的字节
return transferred;
}
}



