21

安全ですか:

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;
   }

   ....
}
  1. 例外のインスタンスを保持する
  2. 必要なときに使う(投げる)

new毎回例外をスローする代わりに?

安全かどうか興味がある

安全とは、つまり、メモリの破損、JVM による追加の例外のスロー、クラスの欠落、不正なクラスのロード (...) がないことを意味します。注: 例外はネットワーク経由でスローされます (リモーティング)。

他の問題 (読みやすさ、インスタンスを維持するコスト) は重要ではありません。

4

6 に答える 6

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
    }
}

次の出力があります。

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

t1最初のテスト ケースのスタック トレースからメソッドが完全に欠落していることに注意してください。有用なコンテキスト情報はまったくありません。

これで、スローの直前に を使用してその情報を入力できます。fillInStackTrace

this.e.fillInStackTrace();
throw this.e;

...しかし、それは自分のために仕事をしているだけです (あなた時々忘れる仕事)。それだけではまったくメリットがありません。また、すべての例外でそれができるわけではありません (一部の例外では、スタック トレースが読み取り専用になります)。


コメントの他の場所で、これは「コードの重複」を避けるためだと言っています。例外ビルダー関数を使用する方がはるかに優れています。

private IllegalStateException buildISE() {
    return new IllegalStateException("le sophisticated way");
}

(お好みでstatic finalどうぞ。)

そして、次のように投げます:

throw buildISE();

これにより、誤解を招くスタック トレースや不要な Exception インスタンスを発生させることなく、コードの重複を回避できます。

これを上記に適用すると、次のようになります。

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
    }
}
$ java ThrowTest
t1:
java.lang.Exception: t1
    ThrowTest.buildEx(ThrowTest.java:24) で
    ThrowTest.t1 (ThrowTest.java:29) で
    ThrowTest.main で (ThrowTest.java:8)
t2:
java.lang.Exception: t2
    ThrowTest.t2 で (ThrowTest.java:34)
    ThrowTest.main で (ThrowTest.java:15)
于 2013-02-26T13:53:39.990 に答える
7

例外が不変でない限り (つまり、enableSuppression=writableStackTrace=false)、安全ではありません。

例外が不変でない場合は、キャッチャーによって変更できます - 新しいスタックトレースを設定するか、抑制された例外を追加します。複数のキャッチャーが例外を変更しようとすると、混乱が生じます。

驚くべきことに、Throwable実際にはスレッドセーフです。したがって、例外が複数のスレッドによって変更された場合、少なくとも壊滅的な障害は発生しません。しかし、論理障害が発生します。

アプリがこの長寿命の例外に抑制された例外を追加し続けると、メモリリークも発生する可能性があります。

于 2013-02-26T16:28:10.430 に答える
3

そうしないもう1つの理由は、スタックトレースが不適切になることです。

コードのどの場所で例外をスローしても、スタックトレースが出力されると、例外がスローされている行ではなく、例外が初期化されている行が表示されます(この特定のケースではWidget.fun()、スタックが予期されていません)。トレースにはWidget.<clinit>)が含まれます。

したがって、あなた(またはあなたのコードを使用する人)は、エラーが実際にどこにあるのかを判断することはできません。

于 2013-02-26T13:52:57.840 に答える
2

例外のスタックトレースを読み取るときに問題が発生する可能性があると考えられます。コードが例外をスローするとスタックトレースがいっぱいになりますが、例外自体のインスタンスに格納されます。getStackTrace()同じ例外のインスタンスを2回スローすると、最後のスタックトレースが返されると思います。何らかの理由で(たとえばマルチスレッド環境で)例外がコード内の異なる場所から2回スローされてから出力される場合、最初のスローからのスタックトレースが間違っている可能性があります。

例外のインスタンスを再利用する理由はないので、再利用することはお勧めしません。

追加情報(エラーメッセージ、エラーコードなど)を保持する一種のコンテナとして例外を使用する場合は、例外がシリアル化可能であるという事実を使用します。スローする前にクローンを作成します。したがって、例外の一意のインスタンスをスローするたびに、そのフィールドは事前に作成されたテンプレートからコピーされます。

于 2013-02-26T13:53:25.093 に答える
2

エラー処理コードに夢中になるのは安全ではありません。シンプルに、わかりやすく、質問する必要のあることは何も書かないでください。エラー処理は、余分なエラーが発生する場所ではありません。

于 2013-02-26T22:59:25.103 に答える
1

例外的な状況だけでなく、通常の状況を説明するために例外を使用するように促すため、これは間違っていると思います。J. Bloch 著『Effective Java second edition』、項目 57、241 ページを参照してください。

また、常にオブジェクトをヒープに保持しているため、オブジェクトの作成は最新の JVM では非常に高速であり、例外がスローされると、おそらく非常に迅速にガベージ コレクションが実行されるため、これは必要ありません。

また、コードが非常に誤解を招く可能性があり、これにより、非常に単純なものに多くのオーバーヘッドが追加されます。

于 2013-02-26T13:52:03.413 に答える