out in System.out.println()

19

首先,如果这是一个非常基础的问题,我很抱歉,我要声明我还是个码农。 在面试中,我被问到如何阐述System.out.println(); 我用以下方式解释:

//this class belongs to java.lang package
class System{
  public static PrintStream out;
}
//this class belongs to java.io package
class PrintStream{
 public void println..
}

我解释过System.out是有效的,因为这是我们在Java中访问静态变量的方式,并且out是PrintStream对象,因此我们可以访问它的方法。总之,

System.out.pritnln(); 

他让我模拟一个类似的程序,我进行了跟踪但没有成功,因为System.out返回null

我的问题是在Java中out对象是在哪里实例化的?如果我没错的话,它是一个预定义的对象。对此应该有仔细的解释。

从技术上讲,我们应该如何称呼out?out是PrintStream类型的变量还是应该说它是PrintStream类型的对象?


1
“System.out返回null”是什么意思? - Oliver Charlesworth
我创建了一个类似于A{ static B out}的类比程序;class B{ }。我尝试打印System.out.println(A.out);但是返回null。 - srk
8个回答

22
当类被实例化时,System.out被初始化为null。这是由System.java中的nullPrintStream()方法设置的,该方法只返回null。
当JVM初始化完成后,它调用initializeSystemClass()方法。该方法调用本地方法setOut0(),该方法将out变量设置为适当的值。
这可能看起来很奇怪,但出于以下原因,这是必要的操作:
out不能静态设置为值,因为System需要是最早加载的类之一(在PrintStream之前)。out必须是final,以便其值不能直接被用户覆盖。由于out不能静态设置,并且是final的,因此我们必须使用native方法setOut0()覆盖语言的语义。
希望这有助于您的理解。

6

System.out是一个普通的静态属性,它是由JVM通过initializeSystemClass()方法在JVM初始化期间设置的。你甚至可以通过简单地调用System.setOut(printOutStream);更改它(虽然不建议这样做),其中printOutStream是你想要用作标准输出的流。

这篇文章详细介绍了System.out.println()的工作原理。


1
实际上,out 是通过 initializeSystemClass() 方法由 JVM 设置的。 - Jivings

0
当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变量。


0

0

甚至不需要去查找网络和文档。我们可以简单地使用 javap java.lang.System 命令,这将为您提供属于 System 类的所有静态字段、方法原型的列表。 只要您知道其包和类名,就可以使用 javap 获取任何 Java 类的详细信息。 out 是在 System 类中定义的公共静态 PrintStream 对象。


0

0
在Oracle Java运行时库中,它是使用registerNatives()本地方法进行实例化的,该方法在加载System类时通过static初始化程序调用。但这是一个实现细节。
您还可以使用System.setOut()直接设置System.out

这是真的吗?因为registerNatives()将本地方法链接到JVM内部方法,我从未见过它用于实例化变量。 - Jivings

-7

System.out.println();

这里的println是PrintStream类的一个对象。我们不能直接为PrintStream类创建对象。Out是System类的一个对象,out被称为System类中的字段。当调用system.out时,它会间接地创建PrintStream类的对象。因此,我们可以使用System.out.println()方法来调用println()。


7
这个答案中有非常多的错误陈述,几乎数不清。请忽略它。 - Ernest Friedman-Hill
3
Surendar,println()并不是一个对象,而是PrintStream类的一个方法。 - Douglas Held
2
你可以直接创建PrintStream类的对象。例如,http://docs.oracle.com/javase/1.5.0/docs/api/java/io/PrintStream.html#PrintStream%28java.io.File%29。 - Douglas Held
2
“out” 不是 System 的一个特定对象,而是 System 的一个字段,正如你所说。它不是“被调用”,而是“存在”,因为在 Java 中,对象不仅仅“拥有”对象,它更加结构化。 - Douglas Held
2
在调用System.out时,PrintStream对象并不是间接创建的;实际上,在JVM启动时就已经存在,并且它的引用被分配给了System.out。(正如在其他帖子中提到的那样) - Douglas Held
2
最后,你拼错了几个名称。在Java中,大小写敏感。你拼错了PrintStream、System和println()。 - Douglas Held

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接