java类库的阅读笔记_jdk1.7.0_40_java.io.FileInputStream

来源:互联网 发布:鲁荣渔2682惨案 知乎 编辑:程序博客网 时间:2024/06/07 05:34

2013 1129:

类:

java.io.InputStream

笔记:

这个抽象类用来定义输入流的读取方式。

三个读数据的方法,read()、read(byte[])、read(byte[], int, int),其中read()返回一个0~255的字节,子类必须实现。另外两个都是在read上的封装。

如果流还有效,没有到结束,又暂时读不到数据,那么就会挂住。

skip(long n)方法则让指向当前索引,往后挪动n个字节。

available()方法返回剩余可读的字节数的评估值。

close()方法关闭流,释放系统资源。

mark(int n)方法,在当前索引位置标记,只要索引在n的范围以内,就可以通过reset方法,使索引重新回到标记位置。

reset(),使索引回到上一次标记的位置,如果索引已经越过了mark的参数n,那么就抛出异常。

markSupported,返回是否支持mark和reset机制。

类:

java.io.FileInputStream

笔记:

这个类的大部分方法都是用native实现的,比如read、skip、available,没有继续向底层窥视的可能了。

不支持mark、reset机制。

FileDescriptor类型的属性,似乎也是和native实现耦合的,没法理解其中的意义。

FileChannel虽然提供了一些很底层的文件读写方法,但是似乎并不好用。

类:

java.io.FileInputStream

方法:

public void close() throws IOException

笔记:

这个方法是供客户端调用的,但是又担心客户端忘记,所以为了保证两种情况下,close方法都能够正确执行,就需要一些特殊的处理。通常客户端遗漏close这种事情,都是由finalize方法来保护的。

典型的做法就是,在FileInputStream里面声明一个私有对象:private Object obj = new Object(){}; 这个对象不会被发布,只被FileInputStream对象引用,所以它的回收和FileInputStream对象的回收,是等价的。在这个对象里面,再定义finalize方法:new Object(){protected void finalize(){}}。然后在其中调用FileInputStream对象的close方法。

但是FileInputStream没有这样做,它直接实现了finalize方法。《Effective Java》指出了这种做法的坏处:如果子类错误的覆盖了finalize方法,那么这个资源释放的保护层就可能失效。对于我这种层次的程序员,这种要求其实极其挑剔了,只是跟着书人云亦云,略过即可,无需纠缠。

close作为资源释放的方法,不能被调用两次,所以close方法里面多做了一点判断:

        synchronized (closeLock) {            if (closed) {                return;            }            closed = true;        }
用一个boolean变量标志是否已经关闭过,并使用closeLock这样一个空对象来进行同步保护。
里面还有一段代码让我费解:

一个ThreadLocal变量:

    private static final ThreadLocal<Boolean> runningFinalize =        new ThreadLocal<>();

finalize中:

            runningFinalize.set(Boolean.TRUE);            try {                close();            } finally {                runningFinalize.set(Boolean.FALSE);            }
close中:

        if ((useCount <= 0) || !isRunningFinalize()) {            close0();        }
isRunningFinalize方法:

    private static boolean isRunningFinalize() {        Boolean val;        if ((val = runningFinalize.get()) != null)            return val.booleanValue();        return false;    }
close方法在没有被显示调用,且FileDescriptor还在被其他文件流使用的情况下,不强制关闭文件资源。

实现这个“没有被显示调用”的逻辑,就是要让close方法能够理解自己是在被finalize线程调用,还是被客户端线程调用。唯一的做法就是在某个地方做一下标记,close方法去读取这个标记。客户端线程是不能保证做标记的,所以只能让finalize线程在调用close前进行标记。

我的想法就是,在FileInputStream中增加一个boolean型私有属性,只有finalize方法可以设置它为true。那么close方法读取这个属性,就知道是不是finalize在调用自己。而且finalize调用完以后,FileInputStream对象就该被回收了,也不存在标记被错误读取的情况。

然而FileInputStream的实现却更加复杂,它用ThreadLocal来存储这个标记。ThreadLocal对象的语意是,每个线程调用ThreadLocal的get方法,都只能获得自己线程曾经set进去的值。也就是说,ThreadLocal对象会根据调用它的线程,区分出存储区域,每个线程塞入自己的属性,读取自己的属性,线程之间不影响。

ThreadLocal对象用在这里意思就是,finalize方法把true塞进finalize线程在runningFinalize中的存储区。如果是finalize线程调用的close,就能够从runningFinalize中取出true,而客户端线程调用的close方法,就只能从runningFinalize中取出空指针。通过这种方式,close方法理解了外围调用自己的线程是哪一个,从而决定是否需要关闭文件资源。

好了,下面的问题我就真的没看懂了,等待大神指点:为什么要在runningFinalize中,存一个Boolean对象,并且在finalize调用close以前,set一个true进去,close以后,还恢复成false。其实我就塞一个Object进去,然后close里面判断它是不是空不就行了?null和非null状态不就可以判断是否finalize线程了吗?何必增加成null、true、false三个状态,实际上false状态根本不可能出现呀?










类:

java.io.FileInputStream

方法:

public void close() throws IOException