在System.java源中,标准输入,输出和错误流被声明为final并初始化为null?

public final static InputStream in = null; public final static PrintStream out = null; public final static PrintStream err = null; 

但正如我们所知,这些流默认连接到控制台并已打开。 System类setIn(),setOut和setErr()中还有一些方法可以重定向流。 当它们被声明为final并设置为初始化值null时,这有什么可能吗?

我编译了以下代码,在调用println()时设置断点并使用netbeans进行调试。 我的目标是通过步入源来确定何时将变量System.in初始化为标准输出。 但似乎输出流输出已经在调用main方法时初始化。

 public static void main(String[] args) { System.out.println("foo"); } 

它们稍后由本机方法SetIn0SetOut0SetErr0

 private static native void setIn0(InputStream in); private static native void setOut0(PrintStream out); private static native void setErr0(PrintStream err); 

initializeSystemClass方法调用,该方法根据JavaDoc 在线程初始化后调用

 FileInputStream fdIn = new FileInputStream(FileDescriptor.in); FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out); FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err); setIn0(new BufferedInputStream(fdIn)); setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true)); setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true)); 

这样做是为了防止“黑客攻击”。 只能通过调用native方法的适当setter来更改这些字段

 private static native void setIn0(InputStream in); private static native void setOut0(PrintStream out); private static native void setErr0(PrintStream err); 

原生方法可以做任何事情,包括更改最终字段

final字段不一定是常数。 它们仍然可以被操作,只是在编译时才会阻止操作,特别是阻止你使用赋值运算符( = )。 请参阅此问题和JLS§17.5.3 ,具体如下:

final字段可以通过reflection和其他依赖于实现的方式进行更改。

这对于反序列化这样的事情是必要的。 这也可能引起一些有趣的警告,因为编译器可以在编译时和运行时优化final字段。 上面链接的JLS就是一个例子。