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");
}
它们稍后将通过本机方法SetIn0
,SetOut0
和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));
这样做是为了防止“黑客入侵”。这些字段只能由调用native
方法的适当设置器更改
private static native void setIn0(InputStream in);
private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);
本机方法可以做所有事情,包括更改最终字段。
final
字段不一定是常数。它们仍然可以被操纵,只是仅在编译时才禁止操纵,特别是通过阻止您使用赋值运算符(=
)。请参阅this question和JLS §17.5.3,具体是:
[
final
字段可以通过反射和其他依赖实现的方式进行更改。
这对于反序列化是必需的。这也可能引起一些有趣的警告,因为编译器可以在编译时和运行时优化final
字段。上面链接的JLS提供了一个示例。