有没有理由为自己设置例外原因?

时间:2012-02-10 19:28:06

标签: java exception

我遇到了一些我正在构建的java库中的地方,其中异常的原因被设置为异常本身。

是否有任何理由将异常引用为其原因?

修改

根据要求,这是一个具体的例子:

enter image description here

4 个答案:

答案 0 :(得分:13)

我经常看到框架或库引发的异常,例如Hibernate或Spring引用本身作为原因(在过程中混淆了调试器GUI)。

我总是想知道为什么他们这样做,因为它似乎是个坏主意。今天,当我尝试将一个序列化为JSON时,它实际上引起了一个问题:bam,enless cycle。

所以我进一步调查了一下:

Throwable的源代码中(此处列出的所有源代码均来自JDK 1.7)我们有:

 /**
     * The throwable that caused this throwable to get thrown, or null if this
     * throwable was not caused by another throwable, or if the causative
     * throwable is unknown.  If this field is equal to this throwable itself,
     * it indicates that the cause of this throwable has not yet been
     * initialized.
     *
     * @serial
     * @since 1.4
     */
    private Throwable cause = this;

现在我特意遇到了一个扩展RuntimeException的异常类的问题,所以我从那里开始。 RuntimeException的构造函数之一:

/** Constructs a new runtime exception with the specified detail message.
     * The cause is not initialized, and may subsequently be initialized by a
     * call to {@link #initCause}.
     *
     * @param   message   the detail message. The detail message is saved for
     *          later retrieval by the {@link #getMessage()} method.
     */
    public RuntimeException(String message) {
        super(message);
    }

上面调用的Exception的构造函数:

 /**
     * Constructs a new exception with the specified detail message.  The
     * cause is not initialized, and may subsequently be initialized by
     * a call to {@link #initCause}.
     *
     * @param   message   the detail message. The detail message is saved for
     *          later retrieval by the {@link #getMessage()} method.
     */
    public Exception(String message) {
        super(message);
    }

上面调用的Throwable的构造函数:

/**
     * Constructs a new throwable with the specified detail message.  The
     * cause is not initialized, and may subsequently be initialized by
     * a call to {@link #initCause}.
     *
     * <p>The {@link #fillInStackTrace()} method is called to initialize
     * the stack trace data in the newly created throwable.
     *
     * @param   message   the detail message. The detail message is saved for
     *          later retrieval by the {@link #getMessage()} method.
     */
public Throwable(String message) {
    fillInStackTrace();
    detailMessage = message;
}

fillInStackTrace是一种原生方法,似乎无法修改原因字段。

您可以看到,除非随后调用initCause方法,否则cause字段永远不会更改this的原始值。

结论:如果使用不带Exception的构造函数创建新的cause(或许多子类中的一个,并且不会覆盖此行为)参数,你不调用initCause方法,异常的原因本身就是!

所以我想这应该是非常常见的事情。

答案 1 :(得分:10)

接受的答案具有误导性,其他答案不完整。所以......

虽然 是错误的设计以传递异常作为其自身的原因,但由于这个原因,它在Throwable实现中是不可能的。原因是在构造期间传入,或者传递给initCause()方法,并且如第二个答案所指出的,后者将导致IllegalArgumentException。

正如第三个答案所指出的,如果你没有提供原因,那么根据Throwable实施,原因将是 this

可能缺少的(给出原始问题)是Throwable的getCause()方法没有返回 this ,如果cause == this,则返回null。因此,虽然你的调试器显示 this 引用是因为它使用反射,但是当使用Throwable的公共接口时你不会看到它,所以它不会成为一个问题

答案 2 :(得分:3)

我来自Throwable的来源:

public synchronized Throwable initCause(Throwable cause) {
    ...
    if (cause == this)
        throw new IllegalArgumentException("Self-causation not permitted");
    ...
}

我看不出设置原因是否可行。

答案 3 :(得分:2)

不,那只是糟糕的设计。如果异常是根本原因,则不需要定义原因。

具有原因的异常是不同异常包装的合法情况。例如,如果创建持久性存储,则可能需要抛出PersistenceExcpetion。那么如果它是一个文件存储,你可以让原因成为IOException。如果它是一个数据库,可能原因是SqlException。等