web-dev-qa-db-ja.com

throw eとthrow new Exception(e)の違いは何ですか?

考慮してください:

_try  {
    // Some code here
} catch (IOException e) {
    throw e;
} catch (Exception e) {
    throw e;
}
_

_throw e_とthrow new Exception(e)の違いは何ですか?

_try  {
   // Some code here
} catch (IOException e) {
   throw new IOException(e);
} catch (Exception e) {
   throw new Exception(e);
}
_
38
Vinaya Nayak

例外の種類を調整する必要がない場合は、再スロー(さらにスロー)同じインスタンスを変更せずに再実行します。

_catch (IOException e) {
    throw e;
}
_

例外タイプを調整する必要がある場合は、必要なタイプのwrap e(原因として)新しい例外にを指定します。

_catch (IOException e) {
    throw new IllegalArgumentException(e);
}
_

私は他のすべてのシナリオをコードのにおいだと考えています。 2番目のスニペットはgoodの例です。


以下は、ポップアップする可能性のある質問への回答です。

なぜ例外を再スローしたいのですか?

あなたはそれを手放すことができます。しかし、それが起こると、このレベルでは何もできなくなります。

メソッドで例外をキャッチしても、まだそのメソッド内にあり、そのスコープ(ローカル変数とその状態など)にアクセスできます。例外を再スローする前に、必要なことをすべて実行できます(たとえば、メッセージをログに記録し、どこかに送信し、現在の状態のスナップショットを作成します)。

例外を調整する必要があるのはなぜですか?

経験則として、

上位層は、下位レベルの例外をキャッチし、代わりに、上位レベルの抽象化の観点から説明できる例外をスローする必要があります。

有効Java-第2版-項目61:抽象化に適した例外をスローする

つまり、ある時点で、あいまいなIOExceptionを目立つMySpecificBusinessRuleExceptionに変換する必要があります。

私はそれを"例外タイプの調整"と呼びました、賢い人たちはそれを例外翻訳と呼びます(例外連鎖、特に)。


明確にするために、愚かな例をいくつか挙げましょう。

_class StupidExample1 {
    public static void main(String[] args) throws IOException {
        try {
            throw new IOException();
        } catch (IOException e) {
            throw new IOException(new IOException(e));
        }
    }
}
_

次のような詳細なスタックトレースになります

_Exception in thread "main" Java.io.IOException: Java.io.IOException: Java.io.IOException
    at StupidExample1.main(XXX.Java:XX)
Caused by: Java.io.IOException: Java.io.IOException
    ... 1 more
Caused by: Java.io.IOException
    at StupidExample1.main(XXX.Java:XX)
_

効果的に削減することができます(そしてすべきです)

_Exception in thread "main" Java.io.IOException
    at StupidExample1.main(XXX.Java:XX)
_

別のもの:

_class StupidExample2 {
    public static void main(String[] args) {
        takeString(new String(new String("myString")));
    }

    static void takeString(String s) { }
}
_

new String(new String("myString"))が_"myString"_のワードバージョンであり、後者にリファクタリングする必要があることは明らかです。

47
Andrew Tobilko
catch (IOException e) {
    throw e;
}

元のスタックトレースのみで元の例外が表示されます。スタックトレースにこの「再スロー」行が表示されないので、透過的です。

catch (IOException e) {
    throw new IllegalStateException(e);
}

作成されたIllegalStateExceptionとそのスタックトレースが、「原因」の元の例外情報とスタックトレースとともに表示されます。新しく作成されたIOExceptionの原因として、スローされる例外を設定します(これからです)。上位層にはIllegalStateExceptionが表示され、それをキャッチすることができます(そのキャッチの原因となる例外はキャッチされません)。

catch (IOException e) {
     throw new IOException();
}

IOException作成の現在のスタックトレースのみが表示され、原因は追加されません。

14
Antoniossss

まあ、基本的に、throw e will "rethrow"すべての元の値-一部のコードフロー。たとえば、セキュリティ上の理由などで非表示にする必要があります。 re-create例外になると、その場所に別のスタックトレースが表示されます-または取得できます。

したがって、一部のデータをマスクするオプションがあると思います(わかりません。たとえば、例外を特別なログに記録できますが、他の診断データをエンドユーザーに渡したいでしょう)。

次のことを少し確認してみましょう。

  • 例外の単なるジェネレーターとしてクラスを1つ作成しました
  • 別のクラスでは、例外を再スローまたは再作成できます
  • その後、私はスタックトレースを出力して結果を比較しています

例外ジェネレーター

public class ExceptionsThrow {
    public static void throwNewException() throws Exception {
        throw new Exception("originally thrown message");
    }
}

例外を再スロー/再作成するためのクラス

  public class Exceptions {

        public static void reThrowException() throws Exception {
            try {
                ExceptionsThrow.throwNewException();
            } catch (Exception e) {
                throw e;
            }
        }

        public static void reCreateNewException() throws Exception {
            try {
                ExceptionsThrow.throwNewException();
            } catch (Exception e) {
                throw new Exception(e);
            }
        }
    }

テストコードの例:

try {
     Exceptions.reThrowException();
} catch (Exception e) {
    System.out.println("1st RETHROW");
    e.printStackTrace();
    System.out.println("===========");
}

try {
    Exceptions.reCreateNewException();
} catch (Exception e) {
    System.out.println("2nd RECREATE");
    e.printStackTrace();
    System.out.println("===========");
}

そして最後に出力:

1st RETHROW
Java.lang.Exception: originally thrown message
    at test.main.stackoverflow.ExceptionsThrow.throwNewException(ExceptionsThrow.Java:5)
    at test.main.stackoverflow.Exceptions.reThrowException(Exceptions.Java:7)
    at test.main.MainTest.main(MainTest.Java:110)
Java.lang.Exception: Java.lang.Exception: originally thrown message===========
2nd RECREATE

    at test.main.stackoverflow.Exceptions.reCreateNewException(Exceptions.Java:17)
    at test.main.MainTest.main(MainTest.Java:118)
Caused by: Java.lang.Exception: originally thrown message
    at test.main.stackoverflow.ExceptionsThrow.throwNewException(ExceptionsThrow.Java:5)
    at test.main.stackoverflow.Exceptions.reCreateNewException(Exceptions.Java:15)
    ... 1 more
===========

この場合、ほとんど同じデータを見ることができますが、新しい例外を作成するために同じ例外を使用したので、いくつかの追加で元のメッセージを見ることができますが、このようにする必要はないので、元の原因を隠すことができます。または、アプリケーションのロジックを公開する必要はありません。たとえば、もう1つの例を確認してみましょう。

  • 元の例外から原因のみを取得しますが、データを上書きします
  • ご覧のとおり、新しく作成された例外には、Originのように完全なスタックトレースが含まれていません

そう:

public static void maskException() throws Exception {
    try {
        ExceptionsThrow.throwNewException();
    } catch (Exception e) {
        throw new Exception("I will dont tell you",e.getCause());
    }
}

そしてその結果:

===========
3rd mask
Java.lang.Exception: I will don't tell you
    at test.main.stackoverflow.Exceptions.maskException(Exceptions.Java:25)
    at test.main.MainTest.main(MainTest.Java:126)
===========

つまり、同じインスタンスで例外を再作成することは少し無意味ですが、そのようにしたい場合があります-データをマスクするか、別のケースが例外の種類を変更する場合は、たとえばI/O例外から一般的な例外などに変更します

現実の世界では、いくつかのWebポータルがPHPスクリプトでのみこの印刷の場合に例外を処理したとき、そして通常、データベースとの接続がたとえば、接続文字列(プレーンテキストのデータベースアドレスと資格情報を含む)がWebブラウザに表示されていました。

3
xxxvodnikxxx

この例は、このコンテキストではあまり意味がありません。同じ例外をスローし、他には何もしないからです。少なくともそれをログに記録することは、はるかに理にかなっています。例外をキャッチして処理またはログに記録しています。処理できない場合は、再スローするか(ケース1)、別の何かにラップします(ケース2)。

ケース1:

public class Main {

    // forced to handle or rethrow again
    public static void main(String[] args) throws IOException {
        read();
    }

    public static void read() throws IOException {
        try {
            readInternal();
        } catch (IOException e) {
            throw e;
        }
    }

    private static void readInternal() throws IOException {
        throw new IOException("Output error");
    }
}

出力には次のようなものが表示されます。

Exception in thread "main" Java.io.IOException: Output error
    at com.alex.Java.Main.readInternal(Main.Java:26)
    at com.alex.Java.Main.read(Main.Java:19)
    at com.alex.Java.Main.main(Main.Java:14)
**Case 2:**

以下のパターンでは、例外のタイプを変更し、元の例外の詳細も保持できます。

try {
   // Some code here
} catch (IOException e) {
    throw new IllegalStateException(e);
}

このケースは、Checked ExceptionUnchecked exceptionに置き換えて、問題のoriginationを保持し、すべての情報(例外チェーンと呼ばれる)を保持したい場合によく発生します。

通常のユースケース:

  • Checked Exceptionは処理できず、呼び出し元に再スローしたくない。チェックされた例外を再スローすると、呼び出し側はそれを処理する必要があります。回復のための通常のケースがない場合、これはあなたがしたいことではありません。
  • IOExceptionのような例外がクライアントに役立つことはほとんどありません。ビジネスドメインの範囲内で、より具体的で明確なものを送信する必要があります。

IOExceptionUnchecked ExceptionDocumentReadExceptionのようにラップすると、実際の状況が明らかになり、呼び出し側に処理を強制しません。

public class Main {

    public static void main(String[] args) {
        read();
    }

    public static void read() {
        try {
            readInternal();
        } catch (IOException e) {
            // log and wrap the exception to a specific business exception
            logger.error("Error reading the document", e);
            throw new DocumentReadException(e);
        }
    }

    private static void readInternal() throws IOException {
        throw new IOException("Output error");
    }
}

出力は次のようになります。

Exception in thread "main" Java.lang.IllegalArgumentException: Error reading the document
    at com.alex.Java.Main.read(Main.Java:21)
    at com.alex.Java.Main.main(Main.Java:14)
Caused by: Java.io.IOException: Output error
    at com.alex.Java.Main.readInternal(Main.Java:26)
    at com.alex.Java.Main.read(Main.Java:19)
    ... 1 more

スタックトレースからわかるように、根本的な原因は、元の問題を見つけるのに役立つロガーであり、ビジネスドメインの例外がユーザーに送信されました。

2
J-Alex

例外をスローすると、インスタンスの作成または宣言と非常によく似たことが行われます。

_throw e_declaresIOExceptionのインスタンス(データを保存せずにデバイスにメモリ空間を割り当てる)なので、事前に作成された例外を宣言しているときに、それをキャッチブロックに再スローします。

_throw new IOException e_initializeIOExceptionの新しいインスタンスなので、catchブロックで新しい例外を作成します他の例外を参照しないでください。

しかしながら

通常、catchブロックで例外をスローしません。ほとんどの場合、次のことを行います。

  1. メソッドで例外をスローすることをコンパイラに警告します(パラメータ宣言の後の_throws IOException_)。
  2. 導入されたデータがいずれにしても検証できない場合は常に、例外をスローします(メソッドの本体でthrow new IOException("Warning text")
  3. Try/catchブロックで、メソッドの呼び出しまたは呼び出しをtryブロックに配置します。 catchブロックにエラーメッセージを入力します。

コード例は次のようになります。

_public static void myMethod() throws IOException{
int prueba=0;
if(prueba>9 //some condition){
    //do a thing
}else
    throw new IOException("This number is not correct");
}//end of method
_
_try{
    myClass.myMethod();
}catch{
    System.out.println("An error has occurred");
}//end of try/catch
_

これは私が例外を正しく行うことを学んできた方法ですが、私はあなたの質問に答えることを願っています。

最初に、例外をカプセル化するために新しい例外タイプを作成する理由を理解する必要があります。

これは、Java例外をキャッチして、コード内の特定のエラーシナリオとしてマッピングする場合に役立ちます。

例えば:

try {
  // read file
} catch (IOException e) {
  throw new MyAppFailedToReadFile(e);
}

したがって、上記のケースでは、アプリ全体の特定のエラーを追跡できます。
これは、1つのクラスでアプリ内のすべての例外を処理し、それらを特定のエラーメッセージにマップする場合に役立ちます。

0
tbraun