是Throwable类的printStackTrace()和异步的

时间:2014-05-19 13:13:30

标签: java exception asynchronous stack-trace

我正在测试一些代码来读取类路径中可用的文件,但最后我得到了与Throwable类的printStackTrace()方法相关的一些有趣的东西。

这是代码:

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

public class Main {

    public static void main(String[] args) throws IOException, URISyntaxException {
        try {
            System.out.println("before-test1");
            test1(); // will throw exception
            System.out.println("after-test1");
        } catch (Exception e) {
            System.out.println("entering catch-1");
            e.printStackTrace();
            System.out.println("exiting catch-1");
        }

        try {
            System.out.println("before-test2");
            test2();
            System.out.println("after-test2");
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            System.out.println("before-test3");
            test3();
            System.out.println("after-test3");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void test1() throws IOException {
        String path = "classpath:/test.txt";

        File file = new File(path);

        String content = FileUtils.readFileToString(file, "UTF-8");

        System.out.println("content = " + content);
    }

    private static void test2() throws IOException {
        String path = "/test.txt";
        InputStream in = Main.class.getResourceAsStream(path);

        String content = IOUtils.toString(in);
        System.out.println("content = " + content);
    }

    private static void test3() throws IOException, URISyntaxException {
        String path = "/test.txt";

        URL url = Main.class.getResource(path);

        File file = new File(url.toURI());

        String content = FileUtils.readFileToString(file, "UTF-8");

        System.out.println("content = " + content);
    }

}

test.txt的内容只有一行,如下所示:

  

anil bharadia

现在这个程序的预期输出如下:

before-test1
entering catch-1
java.io.FileNotFoundException: classpath:\test.txt (The filename, directory name, or volume label syntax is incorrect)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(Unknown Source)
    at org.apache.commons.io.FileUtils.readFileToString(FileUtils.java:874)
    at Main.test1(Main.java:50)
    at Main.main(Main.java:16)
exiting catch-1
before-test2
content = anil bharadia
after-test2
before-test3
content = anil bharadia
after-test3

当我第一次运行这个类时,我得到了与上面相同的输出。

然后我再次运行同一个类,我得到了以下输出:

before-test1
entering catch-1
exiting catch-1
before-test2
content = anil bharadia
after-test2
before-test3
java.io.FileNotFoundException: classpath:\test.txt (The filename, directory name, or volume label syntax is incorrect)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(Unknown Source)
    at org.apache.commons.io.FileUtils.readFileToString(FileUtils.java:874)
    at Main.test1(Main.java:50)
    at Main.main(Main.java:16)
content = anil bharadia
after-test3

在上面的输出中,在执行从其调用的catch块之后以及在test2()执行结束之后打印堆栈跟踪。

所以这让我觉得printStackTrace()方法在某种程度上是异步的。

我试着查看printStackTrace()的来源,发现以下代码对我没有帮助:

public void printStackTrace(PrintStream s) {
        synchronized (s) {
            s.println(this);
            StackTraceElement[] trace = getOurStackTrace();
            for (int i=0; i < trace.length; i++)
                s.println("\tat " + trace[i]);

            Throwable ourCause = getCause();
            if (ourCause != null)
                ourCause.printStackTraceAsCause(s, trace);
        }
    }

我试图谷歌但没有找到任何解释。

当我试图一次又一次地运行同一个类时,在很多情况下我也发现了以下输出:

java.io.FileNotFoundException: classpath:\test.txt (The filename, directory name, or volume label syntax is incorrect)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(Unknown Source)
    at org.apache.commons.io.FileUtils.readFileToString(FileUtils.java:874)
    at Main.test1(Main.java:50)
    at Main.main(Main.java:16)
before-test1
entering catch-1
exiting catch-1
before-test2
content = anil bharadia
after-test2
before-test3
content = anil bharadia
after-test3

这让我更加思考,比如在调用test1()方法之前如何打印堆栈跟踪。这是不可能的。

3 个答案:

答案 0 :(得分:5)

堆栈跟踪打印转到另一个流 - 它们被打印到错误流,而不是标准输出流。这就是为什么有时它们会以不同的顺序显示。

答案 1 :(得分:1)

System.out.println("after-test3");将输入写入out流,printStackTrace()方法将输入写入System.err

public void printStackTrace() {
    printStackTrace(System.err);
}

答案 2 :(得分:1)

错误转到System.err流。 正常输出转到System.out流。

这些流由不同的线程编写,因此如果再次运行,您还可以期望输出中的顺序不同。

要获得所需的输出,您需要更改为:

e.printStacktrace(System.out);

这会将输出重定向到System.out流。