在研究java.lang.System
类的实现时,我发现了这个:
public final static PrintStream out = null;
仅从该定义,我可以看出System.out
的值始终为null
。但是,它在程序启动时初始化(使用JVM自动调用的System.initializeSystemClass()
方法),我可以使用执行安全检查的System.setOut
更改其值,然后将调用委托给setOut0
方法是一种native
方法,用于更改System.out
的值。
为什么我可以更改System.out
的值,即使它已声明为final
?
答案 0 :(得分:6)
这是API中的历史瑕疵。 JLS实际上special-cases System.{in,out,err}
,如果今天设计API,可能会有不同的声明。
这种奇怪的行为在实践中通过忽略System
的Java定义来实现。 System
是核心类之一(以及Runtime
,Object
,系统ClassLoader
和其他一些),它们是Java程序与外部环境的接口。 JVM。为了执行它们的任务,它们必须由JRE以(大部分)本机代码提供,在这种情况下,System
的实际C代码忽略了Java API声明字段{{1}的事实。 }}
答案 1 :(得分:1)
我问如何通过本机代码更改最终字段的值。
它的工作原理与其他任何本机代码更改字段值的方式相同。编译器在编译时检测到final
变量的修改。除了通过调用Field.set()
启动的显式检查之外,没有运行时检查。在任何时候都没有任何运行时内存保护或其他任何涉及,即使它是,它将由JVM而不是操作系统完成。本机代码不必关心字段上的元数据,它只能在内存中设置字节。它完全超出了Java编译器的编译时间检查的范围。
对final
字段的本机代码修改在整个程序中甚至可能不是可见,因为允许编译器在final
字段上执行优化。这就是nullPrintStream()
位与Java 7之前的关系。