在Java中何时/为什么需要调用System.out.flush()?

64
为什么某些流需要被刷新(FileOutputStream和来自Sockets的流),而标准输出流不需要?
每当有人使用System.out PrintStream对象,无论是调用println()还是write(),他们都没有刷新该流。然而,其他程序员习惯于使用PrintStream/PrintWriter与其他流一起调用flush()
最近我向几位程序员提出了这个问题,其中一些人认为Java中有一些后台处理来自动刷新System.out流,但我找不到任何相关文档。
像这样的事情让我想知道简单地调用System.out.println()是否跨平台,因为某些系统可能需要您刷新该流。
4个回答

61

System.out是基于一个PrintStream的,它默认在写入换行符时刷新。

javadoc中可以看到:

autoFlush - 一个布尔值;如果为true,则每当写入字节数组、调用println方法之一或写入换行字符或字节('\n')时,输出缓冲区将被刷新。

因此,你提到的println情况已经得到了明确处理,而使用byte[]write情况也保证会刷新,因为它属于“每当写入字节数组时”。

如果你使用System.setOut替换System.out并且不使用自动刷新流,则必须像其他流一样刷新它。

库代码可能不应该直接使用System.out,但如果它这样做了,那么它应该小心刷新,因为库用户可能会覆盖System.out以使用非刷新流。

任何向System.out写入二进制输出的Java程序都应该在exit之前小心地flush,因为二进制输出通常不包括尾随换行符。


5
只有当autoFlush为真时才有效,但是它是真的吗?我找不到有关System.outautoFlush值的任何文档... - user85421
1
@Carlos,关于 System.outSystem.err 绑定及刷新的问题,取决于启动JVM和启动主类应用程序的系统。当您使用 java 二进制文件来启动一个类时,System.out 被初始化为类似于 new PrintStream(new FileOutputStream(FileDescriptor.out), true, System.getProperty("file.encoding")) 的内容,但其他JVM可能会不同。显然,嵌入小应用程序的JVM会执行不同的操作。 - Mike Samuel
1
在Windows的cmd中,使用隐式的'\n'或显式的.flush()刷新输出是有效的,但在Windows的mintty(也称为MSYS2终端)中则不行。 - veganaiZe
@veganaiZe,你需要写一个 "\r\n" 吗?也就是说,写入 System.lineSeparator() 然后再刷新,这样可靠吗? - Mike Samuel
@MikeSamuel 这是一个已知问题,适用于在Cygwin/MSYS环境之外编译的所有程序。(MinGW!= MSYS)https://github.com/mintty/mintty/wiki/Tips#inputoutput-interaction-with-alien-programs - veganaiZe
显示剩余4条评论

7

当你等不及要显示项目时,请刷新流。

当JVM崩溃时,不刷新流会使项目在显示缓冲区中丢失,这可能会使告诉你JVM为什么崩溃的合理错误消息永久丢失。这使得调试变得更加困难,因为人们往往会说,“但它没有到达这里,因为它应该已经打印出来了”。


错误消息不是未缓冲的吗? - user2153235
1
实现System.err接口的PrintStream通常具有自动刷新选项。如果这样做,写入接口的任何内容都会触发对flush()的额外调用。在JVM初始化后,可以重置System.errSystem.out以捕获输出(这就是JUnit在测试运行期间获取输出的方式)。但我所知道的每个JVM都使用自动刷新的PrintStream初始化System.err。我想你也可以使用这种方法来配置System.out以进行自动刷新。 - Edwin Buck
抱歉,我假设故障排除消息被打印到错误流中。但现在我明白了,响应是关于生成标准输出的。当然,我们可以尝试你建议的将标准输出流配置为自动刷新。我只是一个Java入门者,所以需要进行一些在线研究(并非我现在正在尝试做的事情)。 - user2153235
@user2153235,就我个人而言,我不会重新配置 System.out 以自动刷新缓冲区,这会抵消很多由于未自动刷新而获得的性能提升,并且离开了软件假设的通常路径。 相反的,我会将您的错误消息发送到 System.err 并使用已应用于 System.err 的刷新策略,这可能是自动刷新的(但如果没有,则是因为其他某人/某些事情覆盖了它,通常有原因)。 不要担心犯错,这是从业余爱好者向熟练从业者转变的方式。 - Edwin Buck
@user2153235 对此的一个好的“简单方法”是在主方法的内容周围放置一个try / catch / finally块。在其中,在finally块中刷新System.out。这假定您从不使用System.exit(...)。实际上,使用System.exit(...)会导致程序流控制方面的更多问题,因此避免使用它是一个相当不错的想法。 - Edwin Buck
显示剩余4条评论

6

来自PrintStream文档:

可以选择创建一个PrintStream以便自动刷新; 这意味着在写入字节数组,调用其中一个println方法或写入换行符或字节('\n')后,将自动调用flush方法。

虽然我没有在文档中明确提到,但我理解System.out将执行此自动刷新。


3

默认情况下,System.out是按行缓冲的。因此,如果您调用的是println而不是print,那么这应该不是问题。有关更多信息,请参见此文章


2
抱歉,不必使用println来获取PrintStream的自动刷新。请确认。 - Ed Staub
2
@EdStaub 是的,没错。无论是通过print还是println打印,只要写入了\n字符,它就会刷新。 - Stefan Reich

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