25

使用反射以及http://docs.oracle.com提供的安装程序在已安装的 JDK 中提供的 src.zip ,我发现了以下字段java.lang.System

inouterr被声明为 final,但它们具有各自的(公共)setter 方法,这些方法又调用它们各自的本机部分。

例如,我可以成功地将控制台输出重定向到文件。

一旦我们在 Java 代码中初始化了最终变量,我们就可以准确地设置它。

我的问题是:这条最终规则是否不适用于本机代码?

4

3 回答 3

15

我的问题是:这条最终规则是否不适用于本机代码?

本机代码可以打破final. 它还可以破坏访问规则和基本类型安全,以及其他各种东西。

关于final字段实际上不可变的观点实际上在 JLS 中得到了认可:请参阅JLS 17.5.3。其要点是,如果您确实更改了 a final(例如通过反射),某些保证将不再成立。并且更改final表示编译时间常数的 a 的值可能根本没有效果。

但正如@ignis 指出的那样,System.in/out/err在 JLS 中特别提到“写保护”(JLS 17.5.4)而不是具有正常的final语义。基本上,这意味着即使变量发生变化,final保证也成立。


当无论如何都会有一个setter时,为什么变量是最终的?

在这种特殊情况下,它是 1) 防止System.in/out/err被意外分配破坏,以及 2) 以便更改可以由SecurityManager.

于 2012-12-19T10:53:40.147 回答
2

final使 Java 编译器确保除了初始化之外没有代码尝试更改字段。在 java.lang.System 中是不同的

public static void setOut(PrintStream out) { checkIO(); setOut0(out); }

private static native void setOut0(PrintStream out);

从 javac 的角度来看,没有违规。

于 2012-12-19T10:58:08.167 回答
1

在源代码中,它们没有重新分配方法中的示例out变量setOut()

public static void setOut(PrintStream out) {
 checkIO();
 setOut0(out);
}

他们将传递的流发送到本机代码,并且该代码负责设置该流以供当前使用。所以最终变量没有被重新设置并且这个变量没有在本机代码中使用,无论它传递给本机代码的任何流,它都使用它

于 2012-12-19T10:59:16.340 回答