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方法里面多做了一点判断:
用一个boolean变量标志是否已经关闭过,并使用closeLock这样一个空对象来进行同步保护。synchronized (closeLock) { if (closed) { return; } closed = true; }
里面还有一段代码让我费解:一个ThreadLocal变量:
private static final ThreadLocal<Boolean> runningFinalize = new ThreadLocal<>();
finalize中:
close中:runningFinalize.set(Boolean.TRUE); try { close(); } finally { runningFinalize.set(Boolean.FALSE); }
isRunningFinalize方法:if ((useCount <= 0) || !isRunningFinalize()) { close0(); }
close方法在没有被显示调用,且FileDescriptor还在被其他文件流使用的情况下,不强制关闭文件资源。private static boolean isRunningFinalize() { Boolean val; if ((val = runningFinalize.get()) != null) return val.booleanValue(); return false; }
实现这个“没有被显示调用”的逻辑,就是要让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