16
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 方法时,输出流 out 已经初始化。

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

3 回答 3

11

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

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

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

于 2013-06-16T08:43:14.930 回答
9

它们稍后由本地方法设置SetIn0SetOut0并且SetErr0

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));
于 2013-06-16T08:43:22.260 回答
3

final字段不一定是恒定的。它们仍然可以被操纵,只是操纵只在编译时被阻止,特别是通过阻止你使用赋值运算符(=)。请参阅此问题JLS §17.5.3,特别是:

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

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

于 2013-06-16T09:02:29.113 回答