1
try {
        throw new FileNotFoundException();
    } catch (IOException e) {
        e.printStackTrace();
    }
    catch (Exception e) {
        e.printStackTrace();
    }

2 番目の catch ブロックがコンパイラによって到達不能コードと見なされない理由を誰か教えてもらえますか? ただし、次の場合:

try {
        throw new FileNotFoundException();
    } catch (Exception e) {
        e.printStackTrace();
    }
    catch (IOException e) {
        e.printStackTrace();
    }

2 番目の catch ブロックは到達不能と見なされますか?

結局、FileNotFoundException は IOException の下にあり、ちょうど Exception の下にあります。

編集明確にしてください: コンパイラは、メソッドの throws 句に基づいて、メソッドによって例外がスローされたことを認識します。ただし、特定のタイプの例外 (そのクラスの例外の下) を必ずしも認識しているとは限りません。したがって、メソッドが例外 'A' をスローした場合、コンパイラは実際の例外が 'A' なのか 'A' のサブタイプなのかを判断できません。これは実行時にのみ決定されるためです。ただし、コンパイラは、タイプ「X」の例外がスローされないことを認識しているため、X の catch ブロックを指定するのは誤りです。これは正しいですか?

4

3 に答える 3

4

tryコンパイラは、ブロックからスローされる可能性のある唯一の例外がFileNotFoundException. catchこれが、最初のコード サンプルで 2 番目のブロックが到達不能であると見なされない理由です。

なんらかの理由で、インスタンスRuntimeExceptionの作成中に a がスローされたFileNotFoundException場合 (完全に可能性があります)? じゃあ何?

最初のコード サンプルでは、​​予期しない実行時例外が 2 番目のブロックでキャッチされますが、スローされた場合catchは 1 番目のブロックが処理します。FileNotFoundException

ただし、2 番目のコード サンプルでは、​​すべての例外が 1 番目のcatchブロックでキャッチされ、2 番目のブロックに到達できなくなります。

編集:

catch(Exception e)最初のコードのブロックがコンパイラによって到達不能と見なされない理由をよりよく理解するには、次のコードを試して、2 番目のコードcatchが確実に到達可能であることに注目してください。

public class CustomIOException extends IOException {
    public CustomIOException(boolean fail) {
        if (fail) {
            throw new RuntimeException("the compiler will never know about me");
        }
    }
}

public static void main(String[] args) {
    try {
        throw new CustomIOException(true);
    } catch(IOException e) {
        System.out.println("Caught some IO exception: " + e.getMessage());
    } catch(Exception e) {
        System.out.println("Caught other exception: " + e.getMessage());
    }
}

出力:

他の例外をキャッチしました: コンパイラは私について決して知りません

于 2015-12-10T00:43:54.877 に答える
2

TL;DR

コンパイラは、それがスローFileNotFoundException()される唯一のものではない可能性があると見なします。Exception


説明

JLS§11.2.3 例外チェック

catch 句が (§11.2) チェック済み例外クラス E1 をキャッチでき、catch 句に対応する try ブロックがチェック済み例外クラス E2、E1 のサブクラス、および前の catch 句をスローできる場合、Java コンパイラは警告を発行することが推奨されます。すぐ外側の try ステートメントは、E2 <: E3 <: E1 であるチェック済み例外クラス E3 をキャッチできます。

つまり、catch ブロックによってスローされる可能性のある唯一の例外が であるとコンパイラが判断した場合、FileNotFoundException()2 番目の catch ブロックについて警告が表示されます。ここではそうではありません。

ただし、次のコード

    try{
        throw new FileNotFoundException();
    } catch (FileNotFoundException e){
        e.printStackTrace();
    } catch (IOException e){ // The compiler warns that all the Exceptions possibly 
                             // catched by IOException are already catched even though
                             // an IOException is not necessarily a FNFException
        e.printStackTrace();
    } catch (Exception e){
        e.printStackTrace();
    }

これは、コンパイラが try ブロックを評価して、どの例外がスローされる可能性があるかを判断するために発生します。

コンパイラは警告を出さないためÈxception e、他の例外がスローされる可能性があると見なされます (例: RunTimeException)。これらの RunTimeExceptions を処理するのはコンパイラの仕事ではないので、それを逃してしまいます。


答えの残りの部分は、例外キャッチの背後にあるメカニズムを理解するために読むのが興味深いです。


スキーマ

ご覧のとおり、は階層の上位にあるため、下位の階層Exceptionの後に最後に宣言する必要があります。IOException

ここに画像の説明を入力


IOException投げられたと想像してください。から継承されているExceptionため、IOException IS-A Exception と言えます。そのため、常にブロック内でキャッチされ、ExceptionブロックIOExceptionに到達できなくなります。


実際の例

たとえば、あなたが店にいて、ズボンを選ばなければならないとしましょう。売り手は、最大のものから最小のものまで試してみなければならないとあなたに言います.

自分のサイズに対して大きすぎるパンツを買ってしまうと、自分に合うパンツを見つける機会がなくなります。

別の店に行くと、そこでは正反対のことが起こっています。ズボンは小さいものから大きいものまで選べます。

あなたはあなたの正確なサイズでズボンを買うことに気付くでしょう.

それは少し類推で、少し奇妙ですが、それ自体が物語っています。


Java 7 以降 :マルチキャッチ

Java 7 以降、try ブロックによってスローされる可能性のあるすべてのタイプの例外を 1 つの唯一の catch ブロック内に含めるオプションがあります。

警告 : 階層も尊重する必要がありますが、今回は左から右の順です。

あなたの場合、それは

try{
    //doStuff
}catch(IOException | Exception e){
    e.printStackTrace();
}

次の例は、Java SE 7 以降で有効であり、重複したコードを排除します。

catch (IOException|SQLException ex) {
    logger.log(ex);
    throw ex;
}

catch 句は、ブロックが処理できる例外の種類を指定し、各例外の種類は縦棒 (|) で区切られます。

于 2015-12-10T00:51:33.537 に答える
0

最初のケース:

catch (IOException e) { // A specific Exception
    e.printStackTrace();
}
catch (Exception e) { // If there's any other exception, move here
    e.printStackTrace();
}

ご覧のとおり、最初IOExceptionにキャッチされます。これは、 1 つの特定の例外のみを対象としていることを意味します。次に、2 番目の catch で、 以外の例外を目指しIOExceptionます。したがって、その論理的です。

秒で:

catch (Exception e) { // Move here no matter whatever exception
    e.printStackTrace();
}
catch (IOException e) { // The block above already handles *Every exception, hence this won't be reached.
    e.printStackTrace();
}

最初のブロックで、すべての例外 (そのIOException例外またはその他の例外)をキャッチしました。したがって、すべてがすでに最初のブロックに含まれているため、2 番目のブロックには到達しません。

言い換えれば、最初のケースでは、他の例外よりも特定の例外を対象としています。2 番目のケースでは、最初に特定の例外ではなく、すべて/任意の例外を対象とします。そして、すでにすべての例外を扱ったので、特定の例外を後で持つことは論理的な意味を持ちません。

于 2015-12-10T00:41:44.723 に答える