JAVA例外と例外処理の詳細説明

転載——JAVA 例外と例外処理の詳細
https://www.cnblogs.com/knightsu/p/7114914.html より転載
1. 例外の概要
例外とは何ですか。

異常は正常な状況とは異なり、正常な状況とは異なり、間違いや間違いがあります。Java では、現在のメソッドまたはスコープをブロックする条件を例外と呼びます。

Javaの例外システムとは何ですか?

1. Java のすべての異常クラスは Throwable クラスを継承します。Throwable には主に 2 つのカテゴリがあり、1 つは Error クラス、もう 1 つは Exception クラスです。

2. Error クラスには、仮想マシン エラーとスレッド デッドロックが含まれます。エラーが発生すると、プログラムは完全にハングします。これはプログラム ターミネータと呼ばれます。

3. Exception クラス。一般に「例外」と呼ばれます。主にコーディング上、環境上、ユーザーの操作入力上の問題を指します例外には主に非チェック例外(RuntimeException)とチェック例外(その他一部の例外)の2種類があります

4. RuntimeException には、主に次の 4 つの例外が含まれます (実際には、ここにリストされていない例外も多数あります): NULL ポインター例外、配列添字範囲外例外、型変換例外、および算術例外。RuntimeException は自動的にスローされ、Java 仮想マシンによってキャプチャされます (例外キャプチャ ステートメントを記述しなくても、実行時にエラーがスローされます!!)。そのような例外の大部分はコード自体の問題です。論理的にコードを修正して改善する必要があります。

5. 例外を確認する 例外の原因は、ファイルが存在しない、接続エラーなどさまざまです。"兄弟" RuntimeException とは異なり、例外を処理するにはコードにキャプチャ ステートメントを手動で追加する必要があります。これは、Java 例外ステートメントを学習する際に扱う主要な例外オブジェクトでもあります。

2、try-catch-finally ステートメント
(1) try ブロック: 例外のキャッチを担当します。try で例外が見つかると、プログラムの制御が catch ブロック内の例外ハンドラーに渡されます。

[try ステートメント ブロックは独立して存在できません。catch またはfinally ブロックと共存する必要があります]

(2) キャッチブロック: どのように対処するか? 警告の発行など: プロンプトの表示、構成の確認、ネットワーク接続、エラーのログ記録など。catch ブロックが実行された後、プログラムは catch ブロックから飛び出し、次のコードの実行を続けます。

【catchブロック記述時の注意点】 複数のcatchブロックで扱う例外クラスの場合、例外は上から下へ【近くで処理】されるため、サブクラスをキャッチしてから親クラスをキャッチする処理方法に従ってください。

(3)finally:最後に実行されるコードは、リソースのクローズと解放に使用されます。

================================================= =====================

構文形式は次のとおりです。

コードをコピー
コードをコピー
try { // スローされるいくつかの例外} catch (Exception e) { // 最初の catch // 例外を処理するコード ブロック} catch (Exception e) { // 2 番目の catch複数の catch // 例外を処理するコードのブロックがあります}finally{ // 実行される最終コード}コードをコピー例外が発生すると、プログラムは実行を終了し、例外ハンドラーに渡されます (リマインダーをスローするか、ログの記録など)、例外コードブロックの外側のコードは正常に実行されます。try は多くの種類の例外をスローし、複数の catch ブロックは複数のエラーをキャッチします。












複数の例外処理コード ブロックの順序: 最初にサブクラス、次に親クラス (順序が間違っている場合はコンパイラによってエラーが表示されます)、最後にステートメント ブロックが最終的に実行されるコードを処理します。

================================================= =====================

次に、例を使用して try-catch ステートメントを統合してみましょう~

まず例を見てください。

コードをコピー
コードをコピー
1 package com.hysum.test;
2
3 public class TryCatchTest { 4 /** 5 * 除算器: 除数6 * 結果: 結果7 * ループ中に try-catch キャプチャを実行8 * 各ループ、除算器から 1 を引いた結果=result+100/divider 9 * の場合: 例外をキャッチし、「スローされた例外」を出力し、-1 を返す10 * それ以外の場合: 結果を返す11 * @return 12 */ 13 public int test1(){ 14 int divider= 10; 15 int result=100; 16 try{ 17 while(divider>-1){ 18 divider–; 19 result=result+100/divider; 20 } 21 return result; 22 }catch(Exception e){ 23 e. printStackTrace() ; 24 System.out.println("例外がスローされました!!"); 25 return -1;






















26 }
27 }
28 public static void main(String[] args) { 29 // TODO 自動生成メソッドスタブ30 TryCatchTest t1=new TryCatchTest(); 31 System.out.println("test1 メソッドが実行されました! 値結果の結果: "+t1.test1()); 32 } 33 34 }コードをコピーコードをコピー実行結果:








結果分析: 結果内の赤い単語によってスローされた例外情報は、e.printStackTrace() によって出力されます。これは、ここでスローする例外タイプが算術例外であることを示し、その後に理由が続きます: ゼロによる (0 算術例外が原因) ) に続く 2 行は、この例外を引き起こしたコードの特定の場所を示しています。

上記の例に test2() メソッドを追加して、finally ステートメントの実行ステータスをテストします。

コードをコピー コードをコピー
1
/**
2 * 除算器: 除数
3 * 結果: 結果
4 * ループ中に try-catch キャプチャを実行
5 * 各ループ、除算器から 1 を引く、結果 = 結果 + 100/除算器
6 * If: 例外をキャッチ、印刷out "例外がスローされました", return result=999
7 * それ以外の場合: 結果を返します
8 * 最終的に: 「これはついに、ははは!!」を出力し、結果を出力します
9 * @return
10 */
11 public int test2 () { 12 int divider=10; 13 int result=100; 14 try{ 15 while(divider>-1){ 16 divider–; 17 result=result+100/divider; 18 } 19 return result; 20 }catch( Exception e ){ 21 e.printStackTrace(); 22 System.out.println("例外がスローされました!!"); 23 return result=999; 24 }finally{













25 System.out.println("いよいよです、ははは!!");
26 System.out.println("結果の値は次のとおりです: "+result);
27 }
28
29 }
30
31
32
33 public static void main (String[] args) { 34 // TODO 自動生成されたメソッド スタブ35 TryCatchTest t1=new TryCatchTest(); 36 //System.out.println("test1 メソッドが実行されました! 結果の値は: "+t1 .test1 ()); 37 t1.test2(); 38 System.out.println("test2 メソッドが実行されました!"); 39 }コードをコピーコードをコピー実行結果:








結果の分析: 結果から、try ブロックと catch ブロックのステートメントが実行された後、finally ステートメント ブロックが最後に実行されることがわかります。finally は return 後の式演算の後に実行されます (このとき、演算後の値は返されませんが、返される値が先に保存されます。finally のコードに関係なく、戻り値は変わりません。以前に保存された値)なので、関数の戻り値は最終的に実行される前に決定されます。

興味深い質問があります。上記の test2 メソッドのfinally ステートメント ブロックに return を追加すると、コンパイラは警告を表示します:「finally ブロックは正常に完了しません」

コードをコピー コードを
コピー
1 public int test2(){ 2 int divider=10; 3 int result=100; 4 try{ 5 while(divider>-1){ 6 divider–; 7 result=result+100/divider; 8 } 9 return result; 10 }catch(Exception e){ 11 e.printStackTrace(); 12 System.out.println("Exception throwed!!"); 13 return result=999; 14 }finally{ 15 System.out .println ("ついにこれだ、ははは!!"); 16 System.out.println("結果の値は: "+result); 17 return result;//コンパイラ警告18 } 19 20 }コードをコピー コードをコピー




















問題の分析:finally ブロックの return ステートメントは、try ブロックと catch ブロックの return ステートメントをカバーしている可能性があります。return ステートメントがfinally ブロックに含まれている場合、前の catch ブロックが例外を再スローしたとしても、ステートメント呼び出しcatch ブロックで再スローされた例外は取得されますが、finally ブロックの戻り値が取得され、例外はキャッチされません。

問題の解決: 上記の状況に直面して、実際には、try ブロック内で return ステートメントを使用することも、finally 内で return ステートメントを使用することもせず、finally ステートメントの後に return を使用して、ブロックの終了と戻りを示す方が合理的です。関数。のように:

要約:

1. 例外が発生したか、try および catch での戻り値 return があったかどうかに関係なく、finally ブロック内のコードが実行されます。

2.finally に return を含めないことをお勧めします。そうしないと、プログラムが早期に終了し、try または catch で保存された戻り値が return によって上書きされます。

3. e.printStackTrace()は例外情報を出力できます。

4. 戻り値 -1 は例外をスローする慣用的な方法です。

5. メソッド内に try、catch、finally に return ステートメントがない場合、これら 3 つのステートメント ブロックの外側にある return 結果が呼び出されます。

6.finally は、try で戻った後、呼び出し元の関数に戻る前に実行されます。

3. throw および throws キーワード
Java での例外スローは、通常、throw および throws キーワードを使用して実装されます。

throw ----生成された例外をスローします。これは、例外をスローするアクションです。

これは通常、プログラム内で特定のロジックが発生したときにプログラマが特定のタイプの例外を積極的にスローする場合に使用されます。例:
  構文: throw (例外オブジェクト)、例:

コードを
コピー コードをコピー
1 public static void main(String[] args) { 2 String s = “abc”; 3 if(s.equals(“abc”)) { 4 throw new NumberFormatException(); 5 } else { 6 システム.out.println(s); 7 } 8 //function(); 9 }コードをコピーコードをコピー実行結果:










スレッド「メイン」の例外 java.lang.NumberFormatException
at test.ExceptionTest.main(ExceptionTest.java:67)
throws----スローされる例外のタイプを宣言します (宣言)。

文法形式:

1 public void メソッド名 (パラメータ リスト)
2 throwsException list { 3 // 例外をスローするメソッドを呼び出す、または: 4 throw new Exception(); 5 }は、メソッドが何らかの例外をスローする可能性がある場合の throw に使用されます。スローされる可能性のある例外を、上位層から呼び出すメソッド プログラムに渡します。のように:



コードをコピー コード
をコピー
1 public static void function() throws NumberFormatException{ 2 String s = “abc”; 3 System.out.println(Double.parseDouble(s)); 4 } 5 6 public static void main(String[] args ) { 7 try { 8 function(); 9 } catch (NumberFormatException e) { 10 System.err.println("非データ型は変換できません。"); 11 //e.printStackTrace(); 12 } 13 }コードをコピーするコードをコピーするthrow と throws の比較1. Throw はメソッド関数ヘッダーに表示され、throw は関数本体に表示されます。2. throws は例外の可能性を表しますが、これらの例外は必ずしも発生するわけではありません。throw は例外をスローすることを意味し、throw を実行すると何らかの例外オブジェクトがスローされる必要があります。3. どちらも例外を処理する受動的方法です (ここでの否定は、このメソッドが良くないという意味ではありません)。単に例外をスローするか、スローする可能性がありますが、例外は関数によって処理されず、実際の例外は関数によって処理されます。関数の上位層呼び出し処理。

















例を見てみましょう:

スロー e1、e2、e3 は、このメソッドがこれらの例外をスローする可能性があること、メソッドの呼び出し元がこれらの例外を処理する必要がある可能性があること、およびこれらの例外 e1、e2、e3 が関数本体によって生成される可能性があることをプログラムに伝えるだけです。
Throw は、この場所がこの例外をスローする必要があることを明確にするためです。のように:

コードをコピー コードを
コピー
1 void doA(int a) throws (Exception1,Exception2,Exception3){ 2 try{ 3 … 4 5 }catch(Exception1 e){ 6 throw e; 7 }catch(Exception2 e){ 8 System.out .println("エラーが発生しました!"); 9 } 10 if(a!=b) 11 throw new Exception3("カスタム例外"); 12 }コードをコピー コードをコピー分析: 1. コードには 3 つの例外がある可能性があります。ブロック例外、(例外 1、例外 2、例外 3)。2. Exception1 が発生した場合は、それをキャッチしてスローし、メソッドの呼び出し元が処理します。3. Exception2 例外が生成された場合、メソッドはそれを単独で処理します (つまり、System.out.println("An error happens!");)。したがって、このメソッドは Exception2 をスローしなくなり、 void doA() は Exception1 をスローします。Exception3 の Exception2 を記述する必要はありません。try-catch ステートメントでキャプチャされ、処理されているためです。

















4. Exception3 はメソッドの特定のロジックのエラーであり、プログラマが自ら処理しますが、ロジックが間違っている場合に Exception3 がスローされた場合、メソッドの呼び出し元も例外を処理する必要があります。ここではカスタム例外が使用されます。これについては以下で説明します。

throw および throws キーワードを使用する場合は、次の点に注意する必要があります。

1. スローの例外リストは、1 つの例外をスローすることも、複数の例外をスローすることもできます。例外の各タイプはカンマで区切られます。

2. メソッド本体で、例外をスローするメソッドを呼び出すか、最初に例外をスローします。 throw new Exception () throw を使用して、メソッド本体に「例外をスローする」アクションを示します。

3. メソッドが例外をスローするメソッドを呼び出す場合は、try catch ステートメントを追加してこの例外のキャッチを試みるか、または処理のために上位レベルの呼び出し元に例外をスローするステートメントを追加する必要があります。

カスタム例外

カスタム例外を使用する理由とその利点は何ですか?

1. 作業を行う場合、プロジェクトはモジュールや関数単位で開発されますが、プロジェクト全体を個人で開発することは基本的に不可能ですが、カスタム例外クラスを使用することで、外部例外の表示方法を統一します。

2. 検証や問題が発生した場合、現在のリクエストを直接終了する必要がある場合があります。このとき、カスタム例外をスローすることでリクエストを終了できます。プロジェクトで SpringMVC の新しいバージョンを使用している場合は、それを制御できます。コントローラーを拡張するには、 @ControllerAdvice アノテーションを使用してコントローラー拡張クラスを作成し、カスタム例外をインターセプトし、対応する情報をフロントエンドに応答します。

3. カスタム例外は、「ニュートラル」など、プロジェクト内の特別なビジネス ロジック中に例外をスローすることがあります。システム内の一部のエラーは Java 構文に準拠していますが、プロジェクトのビジネス ロジックには準拠していません。

4. カスタム例外を使用して関連する例外を継承し、処理された例外情報をスローすると、基になる例外を非表示にすることができるため、より安全であり、例外情報がより直感的になります。カスタム例外はスローしたい情報をスローすることができ、スローされた情報から例外の発生場所を識別することができ、例外名に応じて例外がどこにあるかを知ることができ、例外プロンプトの情報に従ってプログラムを修正することができます。 。たとえば、ヌル ポインタ例外 NullPointException では、スタック情報を出力せずに、「xxx が空です」というメッセージをスローして例外の場所を特定できます。

カスタム例外を使用する理由とその利点について説明した後、カスタム例外の問題を見てみましょう。

言うまでもなく、JVM (Java 仮想マシン) がカスタム例外を自動的にスローすることは期待できません。また、JVM がカスタム例外を自動的に処理することも期待できません。例外の検出、例外のスロー、および例外の処理は、プログラマがコード内の例外処理メカニズムを使用して行う必要があります。これにより、開発コストと作業負荷がそれに応じて増加するため、プロジェクトが必要ない場合は、必ずしもカスタム例外を使用する必要はなく、自分で比較検討できる必要があります。

最後に、カスタム例外の使用方法を見てみましょう。

Java では例外をカスタマイズできます。独自の例外クラスを作成する際に留意すべき点がいくつかあります。

すべての例外は Throwable のサブクラスである必要があります。
チェック例外クラスを作成する場合は、Exception クラスを継承する必要があります。
ランタイム例外クラスを作成する場合は、RuntimeException クラスを継承する必要があります。
次のように独自の例外クラスを定義できます。

class MyException extends Exception{ }

例を見てみましょう:

コードをコピー
コードをコピー
1 package com.hysum.test;
2
3 public class MyException extends Exception { 4 /** 5 * エラー コード6 / 7 private String errorCode; 8 9 10 public MyException(){} 11 12 / * 13 *基本例外の構築14 * 15 * @param message 16 * 情報の説明17 */ 18 public MyException(String message) 19 { 20 super(message); 21 } 22 23 24 25 public String getErrorCode() { 26 return errorCode; 27 } 28 29 public void setErrorCode(String errorCode) {


























30 this.errorCode = errorCode;
31 }
32
33
34 }
コードをコピー
コードをコピー
カスタム例外を使用して例外情報をスローします。

コードをコピー
コードをコピー
1 package com.hysum.test;
2
3 public class Main { 4 5 public static void main(String[] args) { 6 // TODO 自動生成メソッドスタブ7 String[] sexs = {"male" , "女性", "中立"}; 8 for(int i = 0; i < sexs.length; i++){ 9 if("中立".equals(sexs[i])){ 10 try { 11 throw new MyException (「中立的な人はいない!」); 12 } catch (MyException e) { 13 // TODO 自動生成された catch ブロック14 e.printStackTrace(); 15 } 16 }else{ 17 System.out.println (sexs[ i]); 18 } 19 } 20 } 21 22 }コードをコピー コードをコピー実行結果:





















これは非常に簡単で、実際のビジネス ニーズに応じて、対応するカスタム例外をスローできます。

4. Java の例外チェーン 例外は
カプセル化する必要がありますが、カプセル化だけでは十分ではなく、例外を渡す必要があります。

例外連鎖は、キャッチされた例外を新しい例外にラップして再スローする例外処理メソッドを指すオブジェクト指向プログラミング手法です。元の例外は、新しい例外の属性 (原因など) として保存されます。これが意味するのは、メソッドは同じ抽象レベルで定義された例外をスローする必要があるが、下位レベルの情報は破棄すべきではないということです。

例外チェーンは次のように理解できます。

キャッチした例外を新しい例外にラップし、元の例外を新しい例外に追加し、新しい例外をスローします。これらは連鎖反応のようなもので、一方が他方を引き起こします。このように、最後の最上位層でスローされる例外情報には、最下層の例外情報が含まれます。

「シナリオ
たとえば、私たちの JEE プロジェクトには通常、永続層、ロジック層、プレゼンテーション層の 3 つの層があります。永続層はデータベースとの対話を担当し、ロジック層はビジネス ロジックの実現を担当し、プレゼンテーション層はUI データの処理を担当します。

このようなモジュールがあります: ユーザーが初めてアクセスしたとき、永続層はユーザーからデータを読み取る必要がありますが、FileNotFoundException は破棄され、ロジック層は何が起こったのかを知る方法がなく、ユーザーにとってわかりやすい処理結果を提供できません。結局のところ、不運なのはプレゼンテーション層です: 例外情報を提供する方法がなく、ユーザーに「エラーが発生しました。何が問題なのかわかりません」と伝えることしかできません。フレンドリーなやり方。

正しい方法は、最初にカプセル化してから転送することです。プロセスは次のとおりです。

1. FileNotFoundException を MyException としてカプセル化します。

2.抛出到逻辑层,逻辑层根据异常代码(或者自定义的异常类型)确定后续处理逻辑,然后抛出到展现层。

3.展现层自行确定展现什么,如果管理员则可以展现低层级的异常,如果是普通用户则展示封装后的异常。


コードのコピー例
コードをコピー
1 package com.hysum.test;
2
3 public class Main { 4 public void test1() throws RuntimeException{ 5 String[] sexs = {"male", " Female", "neutral" }; 6 for(int i = 0; i < sexs.length; i++){ 7 if("neutral".equals(sexs[i])){ 8 try { 9 throw new MyException("中立者なんていない! ”); 10 } catch (MyException e) { 11 // TODO 自動生成された catch ブロック12 e.printStackTrace(); 13 RuntimeException rte=new RuntimeException(e);// RuntimeException 例外にラップされる14 //rte.initCause( e); 15 throw rte;//ラップされた例外をスローします16 } 17 }else{ 18 System.out.println(sexs[i]); 19 } 20 }

















21 }
22 public static void main(String[] args) { 23 // TODO 自動生成メソッドスタブ24 Main m =new Main(); 25 26 try{ 27 m.test1(); 28 }catch (例外 e) { 29 e.printStackTrace(); 30 e.getCause();//元の例外を取得31 } 32 33 } 34 35 }コードをコピーコードをコピー実行結果:















結果の分析: コンソールが最初に元の例外 (e.getCause() によって出力される) を出力し、次に e.printStackTrace() を出力することがわかります。ここで、原因: 元の例外と e.getCause() がわかります。出力は同じです。これは例外チェーンを形成するためです。initCause()の役割は元の例外をラップすることであり、一番下でどのような異常が起こったのか知りたい場合はgetCause()を呼び出すことで元の例外を取得することができます。

》 例外はカプセル化して送信する必要があることが示唆されています
。システムを開発するとき、例外を「飲み込む」のではなく、「裸で」例外をスローするべきではありません。パッケージ化した後にスローするか、例外チェーンを通過させてください。システムをより堅牢にするため。

5. まとめ
Java の例外処理の知識は複雑で理解しにくいですが、ここでは、Java 例外処理を使用する際の適切なコーディング習慣として、次の点をまとめます。

1. ランタイム例外を処理するときは、ロジックを使用して、try-catch 処理を合理的に回避および支援します。

2. 複数の catch ブロックの後に、見逃される可能性のある例外を処理するために catch (Exception) を追加できます。

3. 不確実なコードの場合、潜在的な例外を処理するために try-catch を追加することもできます。

4. 可能な限り例外を処理するようにしてください。印刷するには、単に printStackTrace() を呼び出すことを忘れないでください。

5. 例外に対処する方法は、さまざまなビジネス要件と例外の種類によって異なります。

6. 占有されているリソースを解放するために、finally ステートメント ブロックを追加してみます。

おすすめ

転載: blog.csdn.net/weixin_42169551/article/details/109597626