在System.out中,需要澄清的是什么?

8
我正在查看某人的代码,并发现他反复声明了
PrintStream out = System.out;

后来被称为

out.println("blah");

我认为这很有意思。这是一种常见的做法吗?他只是想要炫耀吗?


6
他也可以很容易地在文件顶部添加一个import static java.lang.System.out;声明。 - ILMTitan
@ILMTitan 这是一个更简洁的解决方案,但当然只适用于Java 5及以上版本。 - Alb
2
@Alb Java 5已经完成了其服务生命周期。 - Tom Hawtin - tackline
3
@Tom 很好,但我仍然需要支持一些Java 1.4代码,所以我认为添加这个警告没有任何问题。 - Alb
9个回答

8

这是一个合理的方法。他基本上为System.out创建了一个别名。有许多优点:

  • 输入更少。
  • 以后更容易更改代码以输出到不同的PrintStream。
  • 可能会有性能提升,尽管它将是微不足道的。

点1,不少于sysout<ctrl-space>,这将插入“System.out.println()”。点2,是的,但这也意味着你可能会犯一个错误,认为“out”表示System.out.println,因为在代码的其他地方都是这样做的。它也更明确,就像不使用短变量名一样。点3-不行。编译器比你聪明。永远不要根据你认为编译器会做什么而做出决策,除非基于测试--你的猜测几乎总是错误的,编译器优化是基于你不会耍花招--编写最清晰的代码。 - Bill K
@Bill K:编译器很聪明,但是字段访问和局部变量访问的语义不同,因此通常情况下无法进行优化。谁能说println()没有改变System.out的值呢?(final字段并不像你想象的那么final——看看System.setout()就知道了。) - Adam Crume
@Adam Crume 我想我应该说编译器和运行时比你想象的要聪明。它可以看到 System.out 没有发生变化,并且内联代码以写入它。实际上,如果它愿意的话,它可以内联整个代码块来执行实际输出 -- 尽管它可能不会这样做,但如果它这样做了,我也不会感到太惊讶。如果您别名 "out",那么它可能会阻止这种运行时优化。基本上,我所说的就是,如果您基于理论性能优势而非经过测试的优势做出编程决策,那么您真的需要重新考虑一下。 - Bill K

3
为了避免在没有IDE的情况下进行小型测试时特别输入System.out.println,我使用import static java.lang.System.out代替。
但是如果您想稍后替换System.out的值,例如将其替换为一个包装器以重定向到文件,则这可能是有意义的。
 PrintStream out = new FilePrintStream("MyLogs.log"); // // System.out

立即将标准输出静音。我再说一遍,这在某些情况下可能有意义,因为为此我会使用一个日志框架。

顺便说一句,最好也声明为final和static:

class YourClass  {
    private final static PrintStream out = System.out;
}

1

这可能是因为通常不建议深入使用其他对象的成员对象。这就像有人伸手进你的口袋从你的钱包里拿钱,而不是向你借一些钱。

这样做可能会有轻微的优势,即能够在需要时更改输出流到文件、套接字或其他位置。因此,他将能够替换:

PrintStream out = System.out;

使用

PrintStream out = new PrintStream(new FileOutputStream(filename));

然而,如果他一遍又一遍地重复声明它,那么他真正失去了上述优势,因为整个重点是将其集中在某个地方,并决定在一个地方输出日志。

请注意,这是一种非常粗糙的方法,真正的标准做法是使用日志记录。Java有自己的包java.util.logging,log4j是另一个非常强大的选择(也非常流行),还有其他选择。


0

这是一个有趣的技巧,但像大多数有趣的技巧一样,最好还是做正确的事情。

一旦你确定你需要日志记录(足够确定以添加类似内容),为什么不直接使用Log4J或其他具有更高灵活性和功能的日志记录系统呢?

给东西起别名可能很有趣,如果你独自一人的话,但即使你打字速度非常慢,每次输入它可以节省你整整5秒钟(相对于在eclipse / netbeans中使用System.out或“sysout +<ctrl-space>”),但第一次有人(也许是你)看到“out”时并不知道它的含义,你将会浪费十倍的时间。

我的意思是,在一个程序中,假设你按照其他帖子的建议重定向“out”以将其发送到文件而不是STDOUT,但在某些类中,“out”仍然会发送到System.out。或者你只是忘记了将其重定向到文件。后来你回来看,说:“嗯,它说:

out.println("WE MADE IT");

但是我在STDOUT中没有看到那行代码,这是怎么回事?

然后你花了4个小时去追踪错误的指示而不是修复bug。


如果你正在使用Eclipse并看到了“out”,你会不会只是按F3键,而不浪费时间地查看它的确切位置?我完全赞成消除重复,并且在每个地方都使用System.out调用的消除是消除重复的一种方式。这个答案https://dev59.com/ZU7Sa4cB1Zd3GeqP3W_m#3041495已经很好地阐述了原因。 - Alb
如果你没有考虑到这一点,那就是整个问题所在。当你与他人合作时,明确胜过简洁。当你独自工作时,你的代码可以尽情地玩乐,但我真的很厌倦在别人的可爱技巧上工作。这种态度正在得到一些Java变量命名约定的喜爱/憎恨。我的意思是,使用yalvn而不是打出yetAnotherLongVariableName可以节省同样多的空间,但有些人开始意识到明确性比简洁更重要(至少在Java世界中——我仍然看到很多“yalvn”c变量名称,好像它们占用了内存或其他东西)。 - Bill K
当然,我欣赏一个体面的方法名称,但在这种情况下,原始方法名称只是“out”。首先,我认为任何人都不应该假设这将是System.out而没有检查它,其次,我认为在每次调用时添加包并不是处理它的最佳方法。例如,将所有System.out调用提取到“printToConsole”或“printToLogStream”方法中比将它们内联留下更好,因为它消除了重复但保留了意图的清晰度。 - Alb
@Alb--它实际上并没有消除重复--当人们不明白这一点时,我感到很困扰。在编程中,需要注意的重复不是重复的打字或显式代码,而是重复的逻辑(这会强制您在多个位置进行更改以修复一个问题)。虽然某种程度上这允许您在一个地方更改日志记录目的地,但我仍然认为,如果您要去到别名外部,为什么不使用真正的记录器包呢? - Bill K

0

如果你需要频繁使用println,那么这是一个快捷方式。我之前在一些地方看到过这样的做法,但我不太喜欢这样做,因为我认为System.out.println更清晰,因为你不需要找出out被分配到了哪里。我设置了一个Eclipse模板,这样我就可以自动完成printlnSystem.out.println的转换,速度非常快。


Eclipse有一个内置的模板“sysout”,所以您甚至不需要创建自己的 :) - benjismith
真的,但我写了很多 Groovy 代码,这样我就可以在任一语言中使用 println。 - Jeff Storey

0

没有。我以前从未见过。

我同意。这还不错...


0

如果您查看文档中的Throwable.printStackTrace,您可以在不带参数的情况下调用它,并将System.out传递给需要PrintStream的版本。

在许多不同的PrintStream对象可能被传递的情况下,这是非常常见的,它使打印代码变得更加简单。


0

这是否是一个好主意存在争议,可能取决于具体情况。

优点:

  • 使应用程序代码看起来更整洁。
  • 可能简化更改输出目标。

缺点:

  • 潜在增加交叉耦合;例如,如果out变量是实例变量或必须作为参数传递。
  • 如果应用程序需要调用System.setOut(),可能会引起麻烦。
  • 如果System.out代码仅用于调试,则难以注意到并删除。事实上,它可能使PMD(等)代码质量检查报告此类问题无效。

值得注意的是,还有其他潜在方法可以实现这一点;例如,将System.out.println(String)替换为实用程序方法printLine(String)可以在不产生交叉耦合的情况下实现类似的效果。


0

由于System.out是一个final变量,他所做的与直接引用System.out是相同的。

除非有人调用了System.setOut()重新分配了System.out

等等,什么?


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