1. InputStream源码
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;
//读取输入流的下一个字节,返回0-255,即字节大小;如果读完了,即没有下一个字节,返回-1;因此该方法可以用于检测是否读取到了输入流的末尾
//该方法会阻塞直到读到输入流的末尾或发生异常,如果发生错误,则抛出IOException异常
public abstract int read() throws IOException;
//读取输入流并存入缓冲数组b中,如果b的大小为0,则读取了0字节,返回0;如果因为达到输入流的末尾,没有字节可以读入,则返回-1;
//其他情况,则至少读取1个字节并存在在b中,返回读取的字节个数,数组元素的值存放的是读取的字节大小
//只要第一个字节不能读取(而不是最后一个)或者输入关闭,或者是发生其他错误,则抛出 IOException
//如果b为空,则抛出 NullPointerException
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
//从输入流中读取len个字节,存放在b中,存放位置从b[off]开始,返回读取的字节数
//实现该方法的思路是反复调用read()方法
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) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
//从输入流中跳过n个字节,返回实际实际跳过的字节数,注意返回是long型
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0; //如果n为负,则返回0,表明跳过0个字节
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
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;
}
}
从源码可以看出,InputStream是个抽象类(因此所有子类必须实现其方法),继承Object类,实现了Closeable接口,主要有3个read()方法,后面两个读入流并存入缓冲数组的方法的本质上还是通过第一个read方法实现的。
出现阻塞的两种情况:一是一直读直到检测到达了输入流的末尾;另外则是抛出异常。
抛出IOException的情况:
(1) 任何原因导致的无法读取第一个字节(不包含到达输入流末尾的情况)
(2) 输入流被关闭
(3) 出现其他I/O错误
抛出NullPointerException的情况:
(1) 数组b为空
注:数组为空和长度为0是不一样的;数组为空代表是一个空引用,不占内存空间;而长度为0,表明是一个空数组,空数组也是一个对象,包含的元素个数为0,占用内存。
2. InputStream类型
InputStream的作用是根据不同数据源产生输入的类,每一种数据源对应相应的InputStream子类,数据源包括:字节数组、String对象、文件、管道、流序列、其他数据源,对应的子类分别为:
ByteArrayInputStream:将缓冲区的字节数组当做InputStream使用
StringBufferInputStream:将String转换成InputStream
FileInputStream:用于从文件读取信息
PipedInputStream:产生用于写入PipedOutput Stream的数据
SequenceInputStream: 将两个或多个InputStream对象转换成单一InputStream
FilterInputStream:抽象类,作为“装饰器”的接口。