InputStream 源码分析:
首先看一下JDK 1.8中的定义:
public abstract class InputStream implements Closeable {
private static final int MAX_SKIP_BUFFER_SIZE = 2048; //丢弃流的暂存容器
public abstract int read() throws IOException; //此方法用于返回流的一个字节
public int read(byte b[]) throws IOException { //此方法用于读取足够长度的流字节到b[]数组中
return read(b, 0, b.length); //查看此方法的实现可以发现实际还是一个字节一个字节的在读
}
public int read(byte b[], int off, int len) throws IOException {
for (; i < len ; i++) { //实际还是一个一个字节的在读
}
}
public long skip(long n) throws IOException {
while (remaining > 0) { //也是一个字节一个字节的在读取从而丢弃
}
}
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() { //用来指定是否支持标记
//正常情况下流一旦读取过了,就无法在读取,但是可以通过设置此值为true配合mark() 和 reset()
//方法来实现重读,实际就是在内存中保留了读取过流的副本,因而如果流比较大时,开启此字段就会
//给内存带来较大的负担
return false;
}
}
通过上面的分析我们可以知道InputStream的基本用法,下面再来看看其子类FileInputStream的定义
public class FileInputStream extends InputStream {
/**
* FileDescriptor 类是文件描述类,可以用来表示开发文件,开放套接字等
* 当FileDescriptor表示某文件时,我们可以通俗的将FileDescriptor看成是该文件。
* 但是,我们不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,
* 则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作。
*/
private final FileDescriptor fd;
private final String path;
/**
* Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。
*/
private FileChannel channel = null;
static {
initIDs();
}
/**
* 我们忽略安全管理器的检查,可以看到,在创建一个文件输入流时,主要做了四件事
* 1.在此之前,调用了一个静态方法initIDs():它的作用是设置类中(也就是FileInputStream)的属性的内存地址偏移量,
* 便于在必要时操作内存给它赋值,而FileInputStream的initIDs方法只设置了fd这一个属性的内存地址偏移量。
* 2.new一个FileDescriptor(文件描述符)
* 里面实际上也有一会调用一个静态方法initIDs()设置了两个属性(fd和handle)的内存地址偏移量
* 其中handle,它是一个文件的句柄,用于操作文件
* 3.附加了可关闭的引用
* 用于使用完文件后可以安全的关闭
* 4.调用了open方法。
* 打开文件,然后把得到的文件句柄赋给了handle属性,而赋值的时候,就要依赖于刚才initIDs所初始化的内存地址偏移量。
* 总结:FileInputStream的初始化过程
* 1、如果FileInputStream类尚未加载,则执行initIDs方法,否则这一步直接跳过。
* 2、如果FileDescriptor类尚未加载,则执行initIDs方法,否则这一步也直接跳过。
* 3、new一个FileDescriptor对象赋给FileInputStream的fd属性。
* 4、打开一个文件句柄。
* 5、将文件句柄赋给FileDescriptor对象的handle属性。
*/
public FileInputStream(File file) throws FileNotFoundException {
fd = new FileDescriptor();
fd.attach(this); //附加一个可关闭的引用 也就是可以执行 fd.closeAll
open(name); //打开文件
}
private native void open0(String name) throws FileNotFoundException; //是native方法
private void open(String name) throws FileNotFoundException {
open0(name); //实际调用的是native方法的开
}
public int read() throws IOException {
return read0(); //同样调用的是native的读方法
}
private native int read0() throws IOException;
private native int readBytes(byte b[], int off, int len) throws IOException; //不知道是批量的还是一个一个的读的
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);
}
}
看了源码之后,真正操作的方法都是本地方法,封装到JVM中去了。