为什么Java中的System.out.println()会打印到控制台?

7

我在网上阅读了几篇关于Java中System.out.println()的解释文章。其中大部分都是这样的:

  • Systemjava.lang包中的一个final类。
  • outSystem类中类型为PrintStreampublic static对象。
  • println()将一行文本输出到输出流。
我的问题是,当我们在代码中使用System.out.println()时,为什么它最终会写入控制台? 这篇文章解释了我们如何通过调用System.setOut()将其写入文件。所以我的问题是,System.setOut()在哪里被调用以将输出重定向到控制台?

我检查了System.setOut()源代码。它调用了一个native方法setOut0()。这个方法在initializeSystemClass()方法中直接调用传递给它fdOut,这是一个在这里定义的FileOutputStream。我没有找到任何传递控制台输出流给setOut0()的地方,也没有发现任何非本地的setOut()的调用。JVM在启动执行时是否在System类之外的其他地方完成了这个操作?如果是,能否有人指出来?


2
它不一定是控制台,而是进程的标准输出流。通常情况下是控制台,但也可能是文件或其他内容。请参见https://en.wikipedia.org/wiki/Standard_streams。 - ewramner
代码中有定义吗? - MsA
fdout 实际上是输出到控制台。我想它就像 Linux/Unix 系统中的 FileOutputStream 一样,因为在这些系统中,I/O 设备也被视为文件。 - samabcde
1
你是在问“当Java启动时,out最初是如何设置的?”和“它是从哪里设置的?”这是我理解你的问题的方式,但似乎答案正在回答其他问题。 - Scratte
显示剩余2条评论
3个回答

6
我的疑问是,当我们在代码中执行System.out.println()时,为什么会最终写入控制台?
在任何符合POSIX标准的shell中,每个进程在shell启动时都会得到三个“标准”流:
- “标准输入”流用于读取输入。 - “标准输出”流用于写入普通输出。 - “标准错误”流用于写入错误输出。
(许多非符合POSIX标准的shell也使用相同的思想。)
对于一个交互式的POSIX shell,默认情况下这些流将从shell的“控制台”读取和写入...这可能是物理控制台,但更可能是用户(最终)桌面机器上的“终端仿真器”。 (细节因情况而异。)
POSIX shell允许您以各种方式重定向标准流;例如:
$ some-command < file     # read stdin from 'file'
$ some-command > file     # write stdout to 'file'
$ some-command 2> file    # write stderr to 'file'
$ some-command << EOF     # read stdin from a 'here' document
  lines of input
  ...
  EOF
$ some-command | another  # connect stdout for one command to
                          # stdin for the next one in a pipeline

等等。如果您这样做,一个或多个标准流将不连接到控制台。

更多阅读:


那么这与问题有什么关系?
当Java程序启动时,System.in/out/err流会连接到由父进程指定的标准输入/输出/错误流;通常是一个shell。
对于System.out,它可能是控制台(无论你如何定义),也可能是文件、另一个程序或/dev/null。但输出去向是由JVM启动方式决定的。
因此,字面上的答案是“因为父进程告诉Java程序这样做”。

在Windows和Linux中,shell如何与jvm通信以设置标准输入/输出?

对于Linux、UNIX、Mac OSX等操作系统,情况如下(我不知道Windows的情况,但我想它应该是类似的):

假设shell要运行aaa > bbb.txt

  1. 父shell分叉出一个子进程......共享父shell的地址空间。
  2. 子进程关闭文件描述符1(标准输出文件描述符)。
  3. 子进程打开“bbb.txt”以便将其写入文件描述符1。
  4. 子进程执行“aaa”命令,并成为“aaa”命令进程。文件描述符0、1和2由exec调用保留。
  5. “aaa”命令开始运行......

当“aaa”命令开始运行时,它发现文件描述符0和2(stdin和stderr)指向与父shell相同的“文件”。文件描述符1(stdout)指向“bbb.txt”。

当使用java命令时,也会发生同样的事情。


我尝试执行 java MyJava abcd。我的 Java 代码中的 sysout 在控制台上打印了 "Hello abcd!!!"。当我执行 java MyJava abcd > textfile.txt 时,我的 Java 代码中的 sysout 将 "Hello abcd!!!" 输出到 textfile.txt 中。所以现在我明白了。现在我正在尝试理解:"因为这是父进程告诉 Java 程序要做的事情"。具体来说,当我们指定 > 与不指定时,究竟会发生什么"技术上"的事情?也就是说,在 Windows 和 Linux 中,shell 如何与 jvm 内部通信以设置标准输入/输出? - MsA
@mercury - 你想要实现什么?你不需要导入任何东西来使用 System.out。这与这个问题和答案有什么关系? - Stephen C

1
它不需要。我们可以重定向到其他地方。以下是重定向到文件的代码:
PrintStream output = new PrintStream(new File("output.txt"));
System.setOut(output);
System.out.println("This will be written to file");

默认情况下,控制台是Java中的标准输出流(System.in)。


0

System.out.println不会打印到控制台,而是打印到标准输出流System.out 是 Java 中表示标准输出流的名称)。标准输出流通常是控制台,但也可以是其他。Java 运行时只是将操作系统的标准输出流包装成一个漂亮的 Java 对象。

非交互式程序通常使用一些标准输入输出通道:它从标准输入流读取输入,对其进行一些操作,并在标准输出流上生成输出。标准输出流可以是控制台,也可以被管道传输到另一个程序的标准输入流或文件中。最终,运行程序的操作系统决定标准输出流输出什么。

例如,在 Unix 终端中你可以这样做:

java -jar your.program.jar > output.txt

将程序的输出存储在文本文件中,或者

java -jar your.program.jar | grep hello

只显示输出中包含“hello”的行。除非您没有指定其他目标,否则标准输出流将写入控制台。


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