System.out被声明为public static final PrintStream out。
但是您可以调用System.setOut()来重新分配它。
哈?如果是final,这怎么可能呢?
(同样的观点也适用于System.in和System.err)
更重要的是,如果您可以更改公共静态最终字段,那么就final为您提供的保证(如果有的话)而言,这意味着什么?(我从来没有意识到也没有想到System.in/out/err表现为final变量)
发布于 2011-05-10 22:28:44
JLS 17.5.4 Write Protected Fields
通常情况下,不能修改最终的静态字段。但是,
System.in、System.out和System.err是最终的静态字段,由于遗留原因,必须允许System.setIn、System.setOut和System.setErr方法对其进行更改。我们将这些字段称为写保护字段,以将它们与普通的最终字段区分开来。
编译器需要将这些字段与其他最终字段区别对待。例如,对普通final字段的读取对同步是“免疫的”:锁或易失性读取中涉及的屏障不必影响从final字段读取的值。由于写保护字段的值可能会发生更改,因此同步事件应该会对它们产生影响。因此,语义规定,除非用户代码在System类中,否则这些字段应被视为不能被用户代码更改的普通字段。
顺便说一句,实际上您可以通过反射对final字段调用setAccessible(true) (或使用Unsafe方法)来改变这些字段。这些技术在反序列化、Hibernate和其他框架等过程中使用,但它们有一个限制:在修改之前已经看到final字段的值的代码不能保证在修改后看到新值。有问题的字段的特殊之处在于,它们没有这种限制,因为编译器以特殊的方式处理它们。
发布于 2011-05-10 22:18:52
Java使用原生方法来实现setIn()、setOut()和setErr()。
在我的JDK1.6.0_20上,setOut()看起来像这样:
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
...
private static native void setOut0(PrintStream out);你仍然不能“正常地”重新赋值final变量,即使在这种情况下,你也不能直接重新赋值这个字段(即你仍然不能编译"System.out = myOut")。本机方法允许一些在常规Java中根本不能做的事情,这就解释了为什么对本机方法有限制,比如要求applet必须签名才能使用本机库。
发布于 2011-05-10 22:21:15
为了扩展Adam所说的内容,这里是impl:
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}并且setOut0被定义为:
private static native void setOut0(PrintStream out);https://stackoverflow.com/questions/5951464
复制相似问题