没有 System.out,如何在控制台上打印输出?

7
我被一道近期面试的问题困扰着。问题是这样陈述的:
假设您无法访问Jdk API中的System类,也不能使用ECHO。您正在JRE 5环境下,如何在控制台上打印任何内容?
实际上,这个问题从这里开始:为什么Java给我们提供了PrintStream对象System.out?它为什么是final的?难道没有其他方法可以在控制台上打印任何内容吗?

6
这个面试问题听起来对我来说相当奇怪。我想知道他们实际上是想了解你的什么情况。 - Jon Skeet
@JonSkeet,假设工作与底层相关,检查申请者是否知道System.out的实际作用是合理的吧? - eis
@eis - 几乎每个人都知道 "System.out 实际上是做什么的?",它已经相当老了。 - KDjava
@pap - 实际上,如果目的是将消息传递给最终用户,那么日志记录框架不是答案。 - Stephen C
@JonSkeet 好的,我想我明白你的观点了。回答这个问题所需的知识是有用的,也许这只是他们引导申请人的方式。 - eis
显示剩余4条评论
6个回答

16

如果你想的话,你可以绕过System对象。System.out会做很多额外的东西(例如处理unicode),所以如果你真的只想得到原始输出和更好的性能,实际上甚至应该绕过它。

import java.io.*;

public class PrintOutTest {
  public static void main(String args[]) throws IOException {
    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new
      FileOutputStream(FileDescriptor.out), "ASCII"), 512);
    out.write("test string");
    out.write('\n');
    out.flush();
  }
}

这在这里已经进一步详细阐述了。


2
+1;甚至不知道FileDescriptor保留了对stdout、stderr、stdin的引用... - home
@StephenC 是的。如果只是想将消息输出到控制台,而标准输出已被重定向,则可以使用stderr(假设未被重定向)。如果两者都已被重定向,则我认为程序不应该能够将文本写入控制台(不考虑直接视频缓冲区、旧式控制台图形等)。 - eis
@StephenC 如果你提到System.console()。writer(),它做同样的事情。 如果stdout已被重定向(像 this),这不会对您有所帮助。 我认为只有在System.out被更改时才能有所帮助。 - eis
@eis - 我认为这取决于平台。例如,在经典的UNIX系统上,进程可以使用“/dev/tty”设备打开到应用程序控制的“终端”的流...而不管任何stdin/out/err重定向。还有“/dev/console”用于系统控制台...在那个时代是有意义的。这两个设备也存在于我的Linux盒子上。 - Stephen C
@StephenC 这在某些平台上是与平台相关的,因为在某些平台上,System.console() 应该返回 null,因为 stdout 已被重定向。但无论在哪个平台上,它仍不是解决方案,因为您不能在写入时使用它(如果 stdout 已被重定向)。 - eis
显示剩余3条评论

4
PrintStream是final的,因为它可以做Windows控制台所能做的一切。同样地,在C#中,“Console”也是一个const类。这些类封装了控制台所能做的所有事情,并且只以一种特定的方式完成。你无法“改进它”,因为在某个时候,由操作系统来处理它。
有很多种方法可以在屏幕上输出内容:
1.编写自己的OutputStream,就像@eis那样做。 2.使用JNI并调用本机DLL/SO中的方法,例如printf()。 3.使用Runtime.getRuntime().exec()并调用echo程序,等等。
+1 @eis

2
你可以使用JDK的日志记录器(java.util.logging.Logger)。
以下是在你的Java类中创建日志记录器的方法。
import java.util.logging.Logger;

private final static Logger LOGGER = Logger.getLogger(MyClass.class .getName()); 

你可以使用log4j来实现相同的目的。

我不认为这能保证写入控制台;例如,如果标准输出已重定向到文件。 - Stephen C

2
面试问题本身就相当扭曲(即人为制造的),解决它需要超出纯Java范畴。在正常情况下,这绝不是你会考虑做的事情。
其他答案已经给出了一些半成品的解决方案……如果你真的、真的必须要这样做的话。(我说半成品,因为它们大多数都没有处理应用程序的stdout / stderr流被重定向到其他位置的情况。而那是这个问题中唯一“真实”的方面……) 如果你可以使用System类(在JDK 6上)……则打印到控制台的正确方法(例如,如果System.out已被重定向)是使用System.console()方法获取一个{{link1:Console}}对象,并使用该对象获取一个Writer
但请注意,如果JVM没有关联的控制台,则console()将返回null
这个问题的起源是:为什么 Java 给我们提供了 PrintStream 对象 System.out?而且它为什么是 final 的?难道没有其他方法在控制台上打印任何东西吗?
答案如下:
1. 为了方便。
2. 避免随机代码意外覆盖它。如果需要担心在 JVM 中运行不受信任的代码,也可以故意覆盖它。但是,受信任的代码实际上可以通过调用 System.setOut(...) 来更改 System.out。这会进行一些幕后魔法来安全地更改 final 变量的状态。(我相信 JIT 编译器知道这一点,并以不同的方式处理那3个 final 变量。)
3. 是的。请参见上述内容和(呕!)其他答案。

“假设您无法访问Jdk API中的System类”是OP所说的。 - maba
你没有访问“System”类的权限 @StephenC - Aniket Inge
Console类是在JDK 6中引入的,所以我想我们不能使用它,因为我们有JDK 5。 - KDjava
@KDjava - 显然,我在谈论实际的解决方案。如果一个开发/生产商使用了即将停止支持的JVM,那么情况会变得非常困难。 - Stephen C
如果“应用程序的stdout / stderr流已重定向到控制台以外的其他位置”,那么System.console()也无法工作,因此这个答案是错误的。唯一适用的情况是仅当System.out/System.err被重定向时。 - eis

1

正如文档所述,

System类包含几个有用的类字段和方法。它不能被实例化。

因此,可以看出,System类是各种有用函数的容器。所有这些字段和方法都是静态的,因此没有任何理由去扩展它。
Java给我们提供了static PrintStream out,因为使用控制台是与程序通信的默认方式。
如果您不同意我的观点,请告诉我。


0
今天我找到了一个实现这个问题的方法。 我使用org.apache.log4j来回答。正如许多人所说,还有许多其他方法。谢谢。
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;

public class HelloByLogger {

    private final static Logger logger = Logger.getLogger(HelloByLogger.class);

    public static void main(String[] args) {

        BasicConfigurator.configure();

        logger.info("From Logger... Ola!");

    }
}

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