Elliotte 在书中(第四版35页,中译版42页)指出,
InputStream.markSupported()
方法的是一个设计上的失误,毕竟,java.io
包中,“仅有两个类始终支持标记(BufferedInputStream
和ByteArrayInputStream
)”。
其实他的说法也没错,就当个类而言,这不得不说是一个非常差的设计。但是,他忽略了一点,Java的IO库使用了装饰器模式,各个IO类并非独立存在,大多数可以相互串接,以添加所需特性。
举个例子,下面的代码中,两次测试同一个变量,但结果却不尽相同。如若按照Elliotte所言,使用一个单独的接口而不是超类中的公有方法,通过instanceof
或反射机制,我们虽然能够得知该对象的顶层类,但却无法真正得知其底层对象是否支持标记。
import java.io.*;
/**
* @author Jekton
*/
public class Tester {
public static void main(String[] args) throws FileNotFoundException {
InputStream in = new FileInputStream("E:\\myWorkspace\\in.txt");
InputStream decorated = new DataInputStream(in);
System.out.println("markSupported() = " + decorated.markSupported());
// inputStream that has an additional decorator
decorated = new DataInputStream(new BufferedInputStream(in));
System.out.println("markSupported() = " + decorated.markSupported());
System.out.println("decorated instanceof DataInputStream = " +
(decorated instanceof DataInputStream));
}
}
程序将打印:
markSupported() = false
markSupported() = true
decorated instanceof DataInputStream = true
这里,很明显,DataInputStream
并不支持标记,如果使用一个独立的接口的话,我们根本无法得知中间还有个BufferedInputStream
(这也是装饰器模式的优点之一)。更有甚者,此时的 decorated
变量根本就没有markSupported()
方法。
Java中,输入流过滤器类都继承 FilterInputStream
(当然,这也包括上面提到的BufferedInputStream
), FilterInputStream
重写(override)了超类 InputStream
的 markSupported()
, 其直接调用底层输入流的 markSupported()
方法
// FilterInputStream
public boolean markSupported() {
return in.markSupported();
}
而在像BufferedInputStream
这一类支持标记的类中,则直接返回l true
// BufferedInputStream
public boolean markSupported() {
return true;
}
如此一来,不管变量的类型如何,只要“装饰器链”中存在一个支持标记的类,markSupported()
都将返回true
。
注:图片截取自[Core Java, Cay S.Horstman, Gary Cornell, 9th edition, volume 2]
后记,Elliotte 的《Java IO》一书,也同样存在此错误。笔者给他发过邮件,可惜他并没有回复。