在java输入流中,InputStream是一个抽象接口,主要实现了read(byte b[],int off,int len)方法,这个方法的实现依赖于read()抽象方法,也就是说,read主要还是依赖于子类的实现。这个方法主要作用是从文件中读取字节数,将其放入到byte数组中。看一下这个方法的实现:
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;
}
这里可以看到read()才是实现读取的关键操作。再看一下read的定义
public abstract int read() throws IOException;
现在来看看FileInputStream的实现。这个类继承了InputStream,主要的字段包含如下几个:
private final FileDescriptor fd;
private final Object closeLock = new Object();
private volatile boolean closed = false;
第一个是文件描述符,用于描述一个打开的连接。第二个是一个锁对象,用于多线程管理。第三个是描述的文件流是否关闭状态。
构造函数主要如下:
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();
}
fd = new FileDescriptor();
fd.incrementAndGetUseCount();
open(name);
}
构造函数的主要作用是创建了一个文件描述符,并使用本地方法打开了这个文件。
这个类的本地方法有如下这些:
private native void open(String name) throws FileNotFoundException;
public native int read() throws IOException;
这里可以看到,打开,读取文件都是使用了本地方法。看一下close方法:
public void close() throws IOException {
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
if (channel != null) {
/*
* Decrement the FD use count associated with the channel
* The use count is incremented whenever a new channel
* is obtained from this stream.
*/
fd.decrementAndGetUseCount();
channel.close();
}
/*
* Decrement the FD use count associated with this stream
*/
int useCount = fd.decrementAndGetUseCount();
/*
* If FileDescriptor is still in use by another stream, the finalizer
* will not close it.
*/
if ((useCount <= 0) || !isRunningFinalize()) {
close0();
}
}
这个方法主要逻辑是,使用锁对象来进行同步,同时设置关闭状态为true。最后通过文件系统的文件描述符来进行操作。close0()方法也是本地的。
需要说明的是,在这里已经开始支持了NIO了,因为这里可以通过getChannel方法获取到FileChannel对象了。具体逻辑在分析NIO源代码时再说。