java:"final"System.out,System.in和System.err?

Jas*_*n S 75 java final

System.out被宣布为public static final PrintStream out.

但你可以打电话System.setOut()重新分配它.

咦?这怎么可能呢final

(同一点适用于System.inSystem.err)

更重要的是,如果你可以改变公共静态最终字段,那么这对于final给你的保证(如果有的话)意味着什么呢?(我从未意识到也没有预料到System.in/out/err表现为final变量)

axt*_*avt 55

JLS 17.5.4写保护字段:

通常,可能不会修改最终的静态字段.但是System.in,System.out并且System.err是最终的静态字段,由于遗留的原因,必须允许通过方法更改System.setIn,System.setOut并且System.setErr.我们将这些字段称为写保护,以区别于普通的最终字段.

编译器需要以不同于其他最终字段的方式处理这些字段.例如,读取普通的最终字段对同步"免疫":锁定或易失性读取中涉及的屏障不必影响从最终字段读取的值.由于可以看到写保护字段的值发生变化,因此同步事件应该对它们产生影响.因此,语义规定这些字段被视为不能由用户代码更改的普通字段,除非该用户代码在System类中.

顺便说一下,实际上你可以final通过调用setAccessible(true)它们(或通过使用Unsafe方法)通过反射来改变字段.这些技术在反序列化期间,通过Hibernate和其他框架等使用,但它们有一个限制:在修改之前看到最终字段值的代码不能保证在修改后看到新值.有关字段的特殊之处在于它们没有这种限制,因为它们由编译器以特殊方式处理.

  • 愿FSM保佑遗留代码,让它以可爱的方式妥协未来的设计! (4认同)

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进行签名以使用本机库.


Ami*_*far 7

为了扩展亚当所说的,这里是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)


vic*_*irk 6

取决于实施.最后一个可能永远不会改变,但它可能是实际输出流的代理/适配器/装饰器,setOut可以例如设置out成员实际写入的成员.但实际上它是本地设置的.