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

问题描述 投票:13回答:3
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");
}
java stream
3个回答
6
投票

它们稍后将通过本机方法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));

8
投票

这样做是为了防止“黑客入侵”。这些字段只能由调用native方法的适当设置器更改

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

本机方法可以做所有事情,包括更改最终字段。


2
投票

final字段不一定是常数。它们仍然可以被操纵,只是仅在编译时才禁止操纵,特别是通过阻止您使用赋值运算符(=)。请参阅this questionJLS §17.5.3,具体是:

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

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

© www.soinside.com 2019 - 2024. All rights reserved.