隐藏类中的System.out.print调用

20

我正在使用一个Java库(jar文件)。该文件的作者添加了大量的System.out.printSystem.out.println。是否有办法为特定对象隐藏这些消息?

*编辑:看起来jar文件似乎创建了一堆线程,每个线程都有自己的System.out.println...

5个回答

43

更改原始的PrintStream,用一个Dummy替换它,在其write()方法上不做任何操作。

完成后别忘了替换原始PrintStream

System.out.println("NOW YOU CAN SEE ME");

PrintStream originalStream = System.out;

PrintStream dummyStream = new PrintStream(new OutputStream(){
    public void write(int b) {
        // NO-OP
    }
});

System.setOut(dummyStream);
System.out.println("NOW YOU CAN NOT");

System.setOut(originalStream);
System.out.println("NOW YOU CAN SEE ME AGAIN");

2
谢谢您的快速回答,但这样做不会隐藏所有的system.outs吗?我想保留程序中的那些输出,但隐藏jar文件创建的那些输出。 - K2xL
1
在调用来自jar的方法之前,请使用System.setOut(dummyStream);。在调用来自jar的方法之后,请使用System.setOut(originalStream);。好吗? - Kerem Baydoğan
1
不完全是这样。JAR文件会创建一堆线程,每个线程都会执行System.out... - K2xL
5
在那种情况下,你需要始终使用 **dummyStream**。当你需要在控制台打印内容时,请使用 **originalStream**。 - Kerem Baydoğan

7

System.setOut(); 可能是你要找的。

该方法与输出流相关,可用于将标准输出流重定向到其他输出流。
System.setOut(new PrintStream(new OutputStream() {
  public void write(int b) {
    // NO-OP
  }
}));

Source


注意:这将停止所有类中的 System.out.printlnSystem.out.print 调用。 - Jeffrey
啊,是的,我刚刚问了上面回答的作者这个问题...所以没有办法只从一个特定的文件中隐藏它吗?我的类中有一些system.outs,我不想隐藏...只是这个jar的作者的那些。 - K2xL
@K2xL 你所说的“只是一个特定的文件”是什么意思?请给我们展示一下你代码中的问题部分。 - Kerem Baydoğan
你可以使用Aspectj来隐藏一个特定的类文件或多个类文件。以上方法非常有效! - Vishal Biyani

4
除了之前发布的方法,我想到了另外两种做法: 1. 使用Java反编译器,移除每个System.out调用,并重新编译jar文件。如果选择此路线,我建议使用这个。 2. 您可以使用堆栈跟踪来查找调用类型。(来源于如何使用堆栈跟踪或反射查找方法的调用者?
List<String> classNames = ...
final PrintStream originalOut = System.out;
PrintStream filterStream = new PrintStream(new OutputStream() {
    public void write(int b) {
         StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
         if(!classNames.contains(stackTraceElements[someNumber].getClassName()){
              originalOut.write(b);
         }
    }
});
System.setOut(filterStream);

其中classNames是jar文件中每个类的完全限定名称,someNumber是您需要查看的正确堆栈位置(可能是1或2)。

/e1
经过一些测试,我相信您需要的someNumber数字是10或11。这是因为System.out.print不直接调用write方法。

写入h的示例堆栈跟踪:
(堆栈跟踪按索引、getClassNamegetFileNamegetMethodName的顺序排列)。

0   java.lang.Thread    null    getStackTrace
1   reflection.StackTrace$1 StackTrace.java write
2   java.io.OutputStream    null    write
3   java.io.PrintStream null    write
4   sun.nio.cs.StreamEncoder    null    writeBytes
5   sun.nio.cs.StreamEncoder    null    implFlushBuffer
6   sun.nio.cs.StreamEncoder    null    flushBuffer
7   java.io.OutputStreamWriter  null    flushBuffer
8   java.io.PrintStream null    write
9   java.io.PrintStream null    print
10  java.io.PrintStream null    println
11  reflection.StackTrace   StackTrace.java main

\n的示例堆栈跟踪:

0   java.lang.Thread    null    getStackTrace
1   reflection.StackTrace$1 StackTrace.java write
2   java.io.OutputStream    null    write
3   java.io.PrintStream null    write
4   sun.nio.cs.StreamEncoder    null    writeBytes
5   sun.nio.cs.StreamEncoder    null    implFlushBuffer
6   sun.nio.cs.StreamEncoder    null    flushBuffer
7   java.io.OutputStreamWriter  null    flushBuffer
8   java.io.PrintStream null    newLine
9   java.io.PrintStream null    println
10  reflection.StackTrace   StackTrace.java main
reflection.StackTrace 是我用来进行测试的主要类。 reflection.StackTrace$1 是我的 filterStream,我在 reflection.StackTracemain 方法中调用了 System.out.println
/e2
这个11或10的差异似乎是因为 println 调用了 printnewLineprint 继续调用 write,而 newLine 直接将换行符写入流中。但这并不重要,因为 jar 不会包含 java.io.PrintStream

有趣的解决方案...看起来过于复杂,但我猜这可能是唯一的解决方案... - K2xL

1
你的问题的解决方案在这里: 这里,使用方法为SysOutController.ignoreSysout();

0
如果你有一个更大的程序,请考虑使用类似的东西。
public abstract class Output {
    private static final PrintStream out = System.out;
    private static final PrintStream dummy = new PrintStream(new OutputStream() {@Override public void write(int b){} });

    private static boolean globalOutputOn = true;
    public static void toggleOutput() {
        System.setOut((globalOutputOn=!globalOutputOn)?dummy:out);
    }

    public static void toggleOutput(boolean on) {
        globalOutputOn = on;
        System.setOut(on?out:dummy);
    }
}

这保证你可以在不同的类中切换输出开和关状态,同时确保稍后能够打开输出。

例如,您可以通过在每个方法开始时调用 toggleOutput(false),在每个方法结束时调用toggleOutput(true) 来使用它。


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