使用反射以及http://docs.oracle.com提供的安装程序在已安装的 JDK 中提供的 src.zip ,我发现了以下字段java.lang.System
,
in, out和err被声明为 final,但它们具有各自的(公共)setter 方法,这些方法又调用它们各自的本机部分。
例如,我可以成功地将控制台输出重定向到文件。
一旦我们在 Java 代码中初始化了最终变量,我们就可以准确地设置它。
我的问题是:这条最终规则是否不适用于本机代码?
使用反射以及http://docs.oracle.com提供的安装程序在已安装的 JDK 中提供的 src.zip ,我发现了以下字段java.lang.System
,
in, out和err被声明为 final,但它们具有各自的(公共)setter 方法,这些方法又调用它们各自的本机部分。
例如,我可以成功地将控制台输出重定向到文件。
一旦我们在 Java 代码中初始化了最终变量,我们就可以准确地设置它。
我的问题是:这条最终规则是否不适用于本机代码?
我的问题是:这条最终规则是否不适用于本机代码?
本机代码可以打破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
.
final
使 Java 编译器确保除了初始化之外没有代码尝试更改字段。在 java.lang.System 中是不同的
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
private static native void setOut0(PrintStream out);
从 javac 的角度来看,没有违规。
在源代码中,它们没有重新分配方法中的示例out
变量setOut()
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
他们将传递的流发送到本机代码,并且该代码负责设置该流以供当前使用。所以最终变量没有被重新设置并且这个变量没有在本机代码中使用,无论它传递给本机代码的任何流,它都使用它