我在System
类中看到,out
对象(类型为PrintStream
)是用null
值初始化的。我们如何调用类似System.out.prinln("");
的方法?In System类out变量初始化方式如下:
package java.lang;
public final class System {
public final static PrintStream out = nullPrintStream();
private static PrintStream nullPrintStream() throws NullPointerException {
if (currentTimeMillis() > 0) {
return null;
}
throw new NullPointerException();
}
}
如上图所示,out
变量由null初始化,这个变量是最终变量,所以它不能进一步初始化,那么我们如何使用"out"变量。
注释中有解释:
/**
* The following two methods exist because in, out, and err must be
* initialized to null. The compiler, however, cannot be permitted to
* inline access to them, since they are later set to more sensible values
* by initializeSystemClass().
*/
initializeSystemClass()
使用本机方法将标准流初始化为非null值。本机代码可以重新初始化被声明为final的变量。
JVM调用初始化它的private static void initializeSystemClass()
方法
请参阅以下两行代码:
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));
这是两种本地方法:
private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);
有一篇很好的文章。
out
对象有一个getter
和setter
。
System类初始化时,会调用其initializeSystemClass()
方法,代码如下:
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
在这段代码中,setOut0()是一个在System.c:中实现的本地函数
JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
jfieldID fid =
(*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
if (fid == 0)
return;
(*env)->SetStaticObjectField(env,cla,fid,stream);
}
这是一个标准的JNI代码,它将System.out
设置为传递给它的参数,该方法调用本地方法setOut0()
,该方法将out变量设置为适当的值。
System.out是final,这意味着它不能在initializeSystemClass()
中设置为其他值,但使用本机代码可以修改final变量。