Java中没有堆栈跟踪的异常

Exception without stack trace in Java

这可能是一个非常幼稚的问题。

我曾经认为Java中的Throwable总是包含堆栈跟踪。这是正确的吗?现在看来,我在没有堆栈跟踪的情况下捕获了exceptions。这有道理吗?是否可以在没有堆栈跟踪的情况下捕获异常?


在没有堆栈跟踪的情况下,可以捕获Java中的可抛出对象:

1
Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace)

Constructs a new throwable with the
specified detail message, cause, suppression enabled or disabled, and
writable stack trace enabled or disabled.

http://docs.oracle.com/javase/7/docs/api/java/lang/throwable.html


对于Java 6:

由于Java 6没有EDCOX1×0的构造函数,所以我们可以用下面的技术来抑制StasTr踪迹填充(借用Scala,从Java异常有多慢就知道了?)

1
2
3
4
5
6
class NoStackTraceRuntimeException extends RuntimeException {
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

用法相同:throw new NoStackTraceRuntimeException ()或它的子类型。

我们也可以通过扩展Throwable来实现这一点:

1
2
3
4
5
6
class NoStackTraceThrowable extends Throwable {
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

但是,一个小问题是你不能再使用catch这些例外,因为它不是Exception的子类型,而是应该捕获NoStackTraceThrowable或它的子类型。

更新:有关不同用例中性能的一些有趣的统计信息,请检查这个so问题


对于Java 7 +,这里有一个异常的例子,其中可以选择性地抑制堆栈跟踪。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SuppressableStacktraceException extends Exception {

    private boolean suppressStacktrace = false;

    public SuppressableStacktraceException(String message, boolean suppressStacktrace) {
        super(message, null, suppressStacktrace, !suppressStacktrace);
        this.suppressStacktrace = suppressStacktrace;
    }

    @Override
    public String toString() {
        if (suppressStacktrace) {
            return getLocalizedMessage();
        } else {
            return super.toString();
        }
    }
}

这可以通过以下方式来证明:

1
2
3
4
5
6
7
8
9
10
try {
    throw new SuppressableStacktraceException("Not suppressed", false);
} catch (SuppressableStacktraceException e) {
    e.printStackTrace();
}
try {
    throw new SuppressableStacktraceException("Suppressed", true);
} catch (SuppressableStacktraceException e) {
    e.printStackTrace();
}

这是基于Apache SystemML的MLContextException,其代码在GitHub上提供,网址为https://github.com/apache/systemml。


在任何异常上抑制stacktrace的最简单方法是

1
throwable.setStackTrace(new StackTraceElement[0]);

如果异常有原因,您可能需要递归地执行相同的操作。

这也尽可能减少了堆栈跟踪的成本

throwable的stacktrace在中初始化

1
Throwable#fillInStackTrace()

这是任何构造函数调用的,因此无法避免。当实际使用stacktrace时,stacktraceElement[]在

1
Throwable#getOurStackTrace()

只有在尚未设置字段throwable.stacktrace时才会发生这种情况。

将stacktrace设置为非空值,可以避免在throwable getourstacktrace()中构造stacktraceElement[],并尽可能减少性能损失。