java I/O InputStream 及子类 FileInputStream 源码分析

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中去了。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值