例外が発生し、例外がキャッチされ、例外がスローされ、カスタム例外が発生します

1.例外をキャッチします。

        Javaでは、例外をスローする可能性のあるステートメントはすべてtry...catchでキャッチできます。例外を引き起こす可能性のあるステートメントをtry{...}に入れ、catchを使用して対応する例外とそのサブクラスをキャッチします。

        1.複数のcatchステートメント

                複数のcatchステートメントを使用でき、各catchは対応するExceptionとそのサブクラスをキャッチします。JVMは例外をキャッチした後、catchステートメントを上から下に一致させます。特定のcatchに一致した後、catchコードブロックを実行し、その後、一致を継続しません。簡単に言えば、複数のcatchステートメントのうち1つだけを実行できます。例えば:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println(e);
    } catch (NumberFormatException e) {
        System.out.println(e);
    }
}

                複数のキャッチがある場合、キャッチの順序は非常に重要であり、サブクラスを最初に記述する必要があります。次に例を示します。

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println("IO error");
    } catch (UnsupportedEncodingException e) { // 永远捕获不到
        System.out.println("Bad encoding");
    }
}

                上記のコードの場合、UnsupportedEncodingExceptionはIOExceptionのサブクラスであるため、キャッチできません。UnsupportedEncodingExceptionがスローされると、catch(IOException e){...}によってキャッチおよび実行されます。したがって、正しいアプローチは、サブクラスフィールドを最初に配置することです。

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
    } catch (IOException e) {
        System.out.println("IO error");
    }
}

       

        2.最後にステートメント 

                例外があるかどうかに関係なく、クリーンアップ作業などのいくつかのステートメントを実行する必要があります。実行ステートメントを数回記述できます。通常の実行を試行し、キャッチごとに再度記述します。次に例を示します。

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
        System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
        System.out.println("END");
    } catch (IOException e) {
        System.out.println("IO error");
        System.out.println("END");
    }
}

                ただし、このコード行はSystem.out.println( "中国がエピデミックとの戦いに勝ちます!!!");ただし、前の呼び出しメソッドがスローされて例外が発生した場合、このコード行は実行されません。 finallyブロックを使用して、エラーの有無にかかわらず実行されることを確認できます。また、上記のコードは次のように書き直すことができます。

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
    } catch (IOException e) {
        System.out.println("IO error");
    } finally {
        System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的
    }
}

                最終的にいくつかの機能に注意してください:

                        1.finallyステートメントは必要ありません。書くこともできなくてもかまいません。

                        2.finallyは常に最後に実行されます。

               例外が発生しない場合、try {...}ブロックは正常に実行され、最後に実行されます。例外が発生した場合、try {...}ブロックの実行が中断され、一致するcatchブロックの実行がジャンプされ、最後に最後に実行されます。いくつかのコードを実行する必要があることを確認するためにfinallyが使用されていることがわかります。

                場合によっては、次のように、catchなしでtry...finally構造を使用することもできます。

void process(String file) throws IOException {
    try {
        ...
    } finally {
        System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的
    }
}

        3.さまざまな例外をキャッチします。

                一部の例外の処理ロジックが同じであるが、例外自体の前に継承関係がない場合は、次のように複数のcatchステートメントを記述する必要があります。

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println("Bad input");
    } catch (NumberFormatException e) {
        System.out.println("Bad input");
    } catch (Exception e) {
        System.out.println("Unknown error");
    }
}

                IOExceptionとNumberFormatExceptionを処理するためのコードは同じであるため、次のように、彼女の二重使用を組み合わせることができます。

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException | NumberFormatException e) { // IOException或NumberFormatException
        System.out.println("Bad input");
    } catch (Exception e) {
        System.out.println("Unknown error");
    }
}

                try ... catch ... finalを使用して、例外の概要をキャッチします。

                        1.複数のcatchステートメントの一致順序は非常に重要であり、サブクラスを最初に配置する必要があります。

                        2. finallyステートメントは、例外の有無にかかわらず実行されることを保証し、オプションです。

                        3. catchステートメントは、複数の非継承例外に一致する場合もあります。

2.例外をスローします。

        1.異常な伝播経路:

                メソッドが例外をスローするときに、現在のメソッドが例外をキャッチしない場合、try ... catchがキャッチされるまで、例外は上位レベルの呼び出しメソッドにスローされます。

public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        process2();
    }

    static void process2() {
        Integer.parseInt(null); // 会抛出NumberFormatException
    }
}

                メソッドの呼び出しスタックは、printStackTrace()を介して出力できます。次に例を示します。

java.lang.NumberFormatException: null
    at java.base/java.lang.Integer.parseInt(Integer.java:614)
    at java.base/java.lang.Integer.parseInt(Integer.java:770)
    at Main.process2(Main.java:16)
    at Main.process1(Main.java:12)
    at Main.main(Main.java:5)

                printStackTrace()は、エラーのデバッグに非常に役立ちます。上記の情報は、次のことを意味します。numberFormatExceptionがjava.lang.Integer.parseIntメソッドでスローされます。下から上に、呼び出し元のレイヤーは次のとおりです。

                        1.main()はprocess1();を呼び出します。

                        2.process1()はprocess2();を呼び出します。

                        3.process2()はInteger.parseInt(String);を呼び出します。

                        4.Integer.parseInt(String)调用Integer.parseInt(String、int);

                Integer.javaのソースコードを見ると、スローする例外メソッドのコードは次のようになっていることがわかります。

public static int parseInt(String s, int radix) throws NumberFormatException {
    if (s == null) {
        throw new NumberFormatException("null");
    }
    ...
}

        2.例外をスローします

                ユーザーが不正な文字を入力するなどのエラーが発生した場合、例外をスローできます。例外をスローするにはどうすればよいですか。上記のInteger.parseInt()メソッドを参照すると、例外のスローは2つのステップに分かれています。

                        1.例外のインスタンスを作成します。

                        2.throwステートメントを使用してスローします。

                例えば:

void process2(String s) {
    if (s==null) {
        NullPointerException e = new NullPointerException();
        throw e;
        //throw new NullPointerException();
    }
}

                実際、例外をスローするほとんどのコードは、上記のコメントアウトまたは次のような行に結合されます。

void process2(String s) {
    if (s==null) {
        throw new NullPointerException();
    }
}

                メソッドが例外をキャッチしてから、キャッチ自体に新しい例外をスローする場合、スローされた例外のタイプを変換するのと同じです。

void process1(String s) {
    try {
        process2();
    } catch (NullPointerException e) {
        throw new IllegalArgumentException();
    }
}

void process2(String s) {
    if (s==null) {
        throw new NullPointerException();
    }
}

                process2()がNullPointerExceptionをスローすると、process1()によってキャッチされ、IllegalArgumentException()がスローされます。IllegalArgumentExceptionがmain()でキャッチされた場合、wineは次のように例外スタックを出力して表示します。

public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {
            throw new IllegalArgumentException();
        }
    }

    static void process2() {
        throw new NullPointerException();
    }
}

                印刷された例外スタックは次のようになります。

java.lang.IllegalArgumentException
    at Main.process1(Main.java:15)
    at Main.main(Main.java:5)

                これは、新しい例外が元の例外または基本例外の情報を失い、元の例外NullPointerExceptionの情報を表示できなくなったことを示しています。完全な例外スタック情報をトレースするには、例外を作成するときに、元のExceptionインスタンスを渡すと、新しいExceptionが元のException情報を保持できます。上記のコードを次のように変更します。

public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {
            throw new IllegalArgumentException(e);
        }
    }

    static void process2() {
        throw new NullPointerException();
    }
}

                上記のコードを実行すると、出力される例外スタックは次のようになります。

java.lang.IllegalArgumentException: java.lang.NullPointerException
    at Main.process1(Main.java:15)
    at Main.main(Main.java:5)
Caused by: java.lang.NullPointerException
    at Main.process2(Main.java:20)
    at Main.process1(Main.java:13)

                :原因:Xxxは、キャッチされたIllegalArgumentExceptionが問題の根本的な原因ではないことを示します。根本的な原因は、Main.process2()メソッドでスローされたNullPointerExceptionです。コード内の生の例外情報を取得するには、Throwable.getCause()メソッドを使用できます。nullが返された場合は、その例外が問題の原因となったルート例外であることを意味します。完全な例外スタック情報を使用して、問題を解決するためにコードをすばやく見つけて変更できます。

例外をキャッチして再度スローするときは、最初の犯罪現場をより早く見つけることができるように、必ず元の例外を保持してください。

                tryまたはcatchブロックで例外をスローした場合でも、finallyブロックは実行されますか?例えば:

public class Main {
    public static void main(String[] args) {
        try {
            Integer.parseInt("abc");
        } catch (Exception e) {
            System.out.println("catched");
            throw new RuntimeException(e);
        } finally {
            System.out.println("finally");
        }
    }
}

                上記のコードの実行結果は次のとおりです。

catched
finally
Exception in thread "main" java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "abc"
    at Main.main(Main.java:8)
Caused by: java.lang.NumberFormatException: For input string: "abc"
    at ...

                1行目はcatchステートメントブロックが入力されたことを示すcatchedを出力し、2行目はfinallyステートメントブロックが実行されたことを示すfinallyを出力するため、catchで例外がスローされても、最終的に実行すると、JVMは最初に最終的に実行し、次に例外をスローします。

        3.例外シールド:

                finallyステートメントの実行時に例外がスローされた場合、catchブロックは引き続きスローできますか?例えば:

public class Main {
    public static void main(String[] args) {
        try {
            Integer.parseInt("abc");
        } catch (Exception e) {
            System.out.println("catched");
            throw new RuntimeException(e);
        } finally {
            System.out.println("finally");
            throw new IllegalArgumentException();
        }
    }
}

                上記のコードを実行し、次のように例外情報を見つけます。

catched
finally
Exception in thread "main" java.lang.IllegalArgumentException
    at Main.main(Main.java:11)

                コンソール出力は、最終的に例外をスローした後、1つの例外しかスローできないため、catchでスローされる予定だった例外が「消える」ことを示しています。スローされない例外は、「マスクされた」例外(抑制された例外)と呼ばれます。まれに、すべての例外を知る必要があるので、すべての例外情報を保存するにはどうすればよいですか?この方法では、最初に元のフィールドを変数に保存してから、Throwable.addSuppressed()を呼び出し、元のフィールドを追加して、最後に次のようにスローします。

public class Main {
    public static void main(String[] args) throws Exception {
        Exception origin = null;
        try {
            System.out.println(Integer.parseInt("abc"));
        } catch (Exception e) {
            origin = e;
            throw e;
        } finally {
            Exception e = new IllegalArgumentException();
            if (origin != null) {
                e.addSuppressed(origin);
            }
            throw e;
        }
    }
}

                catch例外とfinallythrowの両方の例外がマスクされている場合でも、finallythrowed例外には次の例外が含まれています。

Exception in thread "main" java.lang.IllegalArgumentException
    at Main.main(Main.java:11)
Suppressed: java.lang.NumberFormatException: For input string: "abc"
    at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.base/java.lang.Integer.parseInt(Integer.java:652)
    at java.base/java.lang.Integer.parseInt(Integer.java:770)
    at Main.main(Main.java:6)

                抑制されたすべての例外は、Throwable.getSuppressed()を介して取得できます。ほとんどの場合、最後に例外をスローしないでください。したがって、通常、抑制された例外については気にしません。

                

               例外の概要のスロー:

                        1. printStackTrace()を呼び出すと、例外伝播スタックを出力できます。これは、プログラムのデバッグに非常に効果的です。

                        2.例外がキャッチされ、新しい例外が再度スローされた場合、元の例外情報を保持する必要があります。

                        3.通常、finallyで例外をスローしません。最終的に例外がスローされた場合は、元の例外を元の例外に追加する必要があります。呼び出し元は、Throwable.getSuppressed()を介して、追加されたすべての抑制された例外を取得できます。

3.カスタム例外:

                コードで例外をスローする必要がある場合は、JDKですでに定義されている例外を使用してみてください。例:パラメータタイプが無効である場合、IllegalArgumentExceptionをスローする必要があります。

static void process1(int age) {
    if (age <= 0) {
        throw new IllegalArgumentException();
    }
}

                もちろん、大規模なプロジェクトでは、新しい例外タイプをカスタマイズできますが、適切な継承システムを維持することが非常に重要です。通常は、例外を「ルート例外」としてカスタマイズしてから、さまざまなサービスを派生させます。例外のタイプ。ルート例外は適切な例外から派生する必要があります。通常、RuntimeExceptionから派生することをお勧めします。

public class BaseException extends RuntimeException {
}

                他のすべての例外タイプは、ルート例外から派生させることができます。

public class UserNotFoundException extends BaseException {
}

public class LoginFailedException extends BaseException {
}

...

                カスタムルート例外は、複数のコンストラクター(できればRuntimeExceptionに対応する)を提供する必要があります。

public class BaseException extends RuntimeException {
    public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

    public BaseException(String message) {
        super(message);
    }

    public BaseException(Throwable cause) {
        super(cause);
    }
}

                上記のコンストラクターのRuntimeExceptionのネイティブ実装を参照してください。このようにして、例外がスローされたときに、適切なコンストラクターを選択できます。

                カスタム例外の概要:

                        1.例外がスローされた場合は、JDKですでに定義されている例外タイプを可能な限り再利用します。

                        2.例外システムをカスタマイズするときは、RuntimeExceptionクラスからルート例外を派生させてから、ルート例外から他のタイプの例外を派生させることをお勧めします。

                        3.例外をカスタマイズするときは、RuntimeExceptionクラスを模倣し、複数の構築メソッドを提供するようにしてください。

おすすめ

転載: blog.csdn.net/chen_kai_fa/article/details/123614980
おすすめ