存储Exception实例并重用它是否安全?

Jos*_* MN 20 java exception-handling exception

是否安全:

public class Widget {

    private static final IllegalStateException LE_EXCEPTION
            = new IllegalStateException("le sophisticated way");

    ...

   public void fun() {
      // some logic here, that may throw
      throw LE_EXCEPTION;
   }

   ....
}
Run Code Online (Sandbox Code Playgroud)
  1. 保留异常的实例
  2. 在需要时使用它(扔)

而不是new每次都抛出异常?

我感兴趣的是它是否安全

安全我的意思是:没有内存损坏,没有JVM抛出的额外异常,缺少类,没有加载坏类(...).注意:异常将通过网络抛出(远程处理).

其他问题(可读性,保持实例的成本)并不重要.

T.J*_*der 20

这取决于你对"安全"的定义.该异常将给出误导性的堆栈跟踪,我不称之为"安全".考虑:

public class ThrowTest {
    private static Exception e = new Exception("t1"); // Line 2

    public static final void main(String[] args) {
        ThrowTest tt;

        tt = new ThrowTest();
        try {
            tt.t1();
        }
        catch (Exception ex) {
            System.out.println("t1:");
            ex.printStackTrace(System.out);
        }
        try {
            tt.t2();                                  // Line 16
        }
        catch (Exception ex) {
            System.out.println("t2:");
            ex.printStackTrace(System.out);
        }
    }

    private void t1() 
    throws Exception {
        throw this.e;
    }

    private void t2() 
    throws Exception {
        throw new Exception("t2");                    // Line 31
    }
}
Run Code Online (Sandbox Code Playgroud)

这有输出:

$ java ThrowTest
t1:
java.lang.Exception: t1
    at ThrowTest.<clinit>(ThrowTest.java:2)
t2:
java.lang.Exception: t2
    at ThrowTest.t2(ThrowTest.java:31)
    at ThrowTest.main(ThrowTest.java:16)

请注意t1在第一个测试用例中,如何从堆栈跟踪中完全丢失该方法.根本没有有用的上下文信息.

现在,您可以使用fillInStackTrace在抛出之前填写该信息:

this.e.fillInStackTrace();
throw this.e;
Run Code Online (Sandbox Code Playgroud)

......但那只是为自己工作(你有时忘记工作).根本没有任何好处.并非所有异常都允许您这样做(一些例外使堆栈跟踪成为只读).


你在评论的其他地方已经说过,这是为了避免"代码重复".你多大最好有一个例外生成器功能:

private IllegalStateException buildISE() {
    return new IllegalStateException("le sophisticated way");
}
Run Code Online (Sandbox Code Playgroud)

(static final如果你愿意可以.)

然后像这样抛出它:

throw buildISE();
Run Code Online (Sandbox Code Playgroud)

这避免了代码重复,而不会误导堆栈跟踪和不必要的异常实例.

以下是适用于上述内容的内容:

public class ThrowTest {

    public static final void main(String[] args) {
        ThrowTest tt;

        tt = new ThrowTest();
        try {
            tt.t1();                                   // Line 8
        }
        catch (Exception ex) {
            System.out.println("t1:");
            ex.printStackTrace(System.out);
        }
        try {
            tt.t2();                                   // Line 15
        }
        catch (Exception ex) {
            System.out.println("t2:");
            ex.printStackTrace(System.out);
        }
    }

    private static final Exception buildEx() {
        return new Exception("t1");                    // Line 24
    }

    private void t1() 
    throws Exception {
        throw buildEx();                               // Line 29
    }

    private void t2() 
    throws Exception {
        throw new Exception("t2");                     // Line 34
    }
}
Run Code Online (Sandbox Code Playgroud)
$ java ThrowTest
t1:
java.lang.Exception: t1
    at ThrowTest.buildEx(ThrowTest.java:24)
    at ThrowTest.t1(ThrowTest.java:29)
    at ThrowTest.main(ThrowTest.java:8)
t2:
java.lang.Exception: t2
    at ThrowTest.t2(ThrowTest.java:34)
    at ThrowTest.main(ThrowTest.java:15)


irr*_*ble 6

它是不安全的,除非异常是不可变的(即enableSuppression = writableStackTrace = false).

如果异常不是不可变的,则可以通过捕获器修改它 - 设置新的堆栈跟踪或添加抑制的异常.如果有多个捕手尝试修改异常,那就会出现混乱.

令人惊讶的Throwable是,对于上帝知道什么,实际上是线程安全的.因此,如果多个线程修改了异常,那么至少不会发生灾难性故障.但是会出现逻辑上的失败.

如果应用程序不断向此长期异常添加抑制异常,则也可能发生内存泄漏.