System.out
被宣布为public static final PrintStream out
.
但你可以打电话System.setOut()
重新分配它.
咦?这怎么可能呢final
?
(同一点适用于System.in
和System.err
)
更重要的是,如果你可以改变公共静态最终字段,那么这对于final
给你的保证(如果有的话)意味着什么呢?(我从未意识到也没有预料到System.in/out/err表现为final
变量)
axt*_*avt 55
通常,可能不会修改最终的静态字段.但是
System.in
,System.out
并且System.err
是最终的静态字段,由于遗留的原因,必须允许通过方法更改System.setIn
,System.setOut
并且System.setErr
.我们将这些字段称为写保护,以区别于普通的最终字段.编译器需要以不同于其他最终字段的方式处理这些字段.例如,读取普通的最终字段对同步"免疫":锁定或易失性读取中涉及的屏障不必影响从最终字段读取的值.由于可以看到写保护字段的值发生变化,因此同步事件应该对它们产生影响.因此,语义规定这些字段被视为不能由用户代码更改的普通字段,除非该用户代码在
System
类中.
顺便说一下,实际上你可以final
通过调用setAccessible(true)
它们(或通过使用Unsafe
方法)通过反射来改变字段.这些技术在反序列化期间,通过Hibernate和其他框架等使用,但它们有一个限制:在修改之前看到最终字段值的代码不能保证在修改后看到新值.有关字段的特殊之处在于它们没有这种限制,因为它们由编译器以特殊方式处理.
Ada*_*kin 28
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);
Run Code Online (Sandbox Code Playgroud)
您仍然无法"正常"重新分配final
变量,即使在这种情况下,您也不会直接重新分配字段(即您仍然无法编译" System.out = myOut
").本机方法允许您在常规Java中无法做到的一些事情,这解释了为什么本机方法存在限制,例如要求对applet进行签名以使用本机库.
为了扩展亚当所说的,这里是impl:
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
Run Code Online (Sandbox Code Playgroud)
和setOut0定义为:
private static native void setOut0(PrintStream out);
Run Code Online (Sandbox Code Playgroud)