目录
从本篇博客开始会逐一讲解InputStream / OutputStream及其对应子类的使用与实现细节。
一、InputStream
InputStream是一个抽象类而非接口类,该类实现的接口如下:
其中AutoCloseable接口是JDK 1.7引入的,try代码块依赖此接口实现资源自动释放;Closeable接口是JDK 1.5引入的,覆写了AutoCloseable定义的close方法,将其变成public并抛出IOException异常。InputStream的子类众多,重点关注io包下的子类,如下:
其中带红点的都是内部私有的类,其他public子类在后续的博客中会陆续探讨。
InputStream定义的方法如下:
其中子类必须实现的抽象方法只有一个,用于读取流中下一个字节的无参的read方法,返回值在0到255之间,如果到达流末尾了则返回-1。该方法会阻塞当前线程,直到读取到数据,到了流末尾或者抛出了异常,其定义如下:
重载的两个read方法和skip方法都是基于无参的read方法实现,其他方法都是无意义的空实现,如下:
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
//从流中读取字节数据,将读取到的第一个字节到b的off处,然后依次读取len个,返回实际读取的字节数
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;
}
//返回值在0到255之间,所以可以强转成byte
b[off] = (byte)c;
int i = 1;
try {
//i从1开始,读取len-1个字节
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
//跳过指定的字节数,返回实际跳过的字节数
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
//MAX_SKIP_BUFFER_SIZE的值是2048
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;
}
二、FileInputStream
1、定义
FileInputStream继承自InputStream,包含的属性如下:
/* 文件描述符 */
private final FileDescriptor fd;
/**
* 文件路径
*/
private final String path;
/*
* NIO使用的FileChannel
*/
private FileChannel channel = null;
private final Object closeLock = new Object();
//文件是否已关闭的标识
private volatile boolean closed = false;
其构造方法如下:
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
public FileInputStream(File file) throws FileNotFoundException {
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
//检查文件访问权限
security.checkRead(name);
}
if (name == null) {
throw new NullPointerException();
}
if (file.isInvalid()) { //检查路径是否合法
throw new FileNotFoundException("Invalid file path");
}
//初始化fd
fd = new FileDescriptor();
fd.attach(this);
path = name;
//打开文件描述符
open(name);
}
//适用于从已经打开的fdObj中读取数据,即多个FileInputStream实例共享一个fdObj
//可以通过getFD方法获取当前绑定的FileDescriptor实例
public FileInputStream(FileDescriptor fdObj) {
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
throw new NullPointerException();
}
if (security != null) {
security.checkRead(fdObj);
}
fd = fdObj;
path = null;
/*
*将fd同当前File绑定
*/
fd.attach(this);
}
private void open(String name) throws FileNotFoundException {
//本地方法实现
open0(name);
}
FileInputStream改写了父类所有方法的实现,其核心都通过本地方法实现,如下:
public int read() throws IOException {
//本地方法
return read0();
}
public int read(byte b[]) throws IOException {
//本地方法
return readBytes(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
//本地方法
return readBytes(b, off, len);
}
public long skip(long n) throws IOException {
//本地方法
return skip0(n);
}
public int available() throws IOException {
//本地方法
return available0();
}
public void close() throws IOException {
synchronized (closeLock) {
if (closed) {
//获取锁,如果已关闭则返回
return;
}
//未关闭,将closed置为true
closed = true;
}
if (channel != null) {
channel.close();
}
fd.closeAll(new Closeable() {
public void close() throws IOException {
//本地方法
close0();
}
});
}
下面逐一说明各本地方法的实现细节,位于OpenJDK jdk\src\share\native\java\io\FileInputStre