Javaの例外は、エラーを特定して応答するためにJavaによって提供される一貫性メカニズムです。
Java例外メカニズムは、プログラムの例外処理コードを通常のビジネスコードから分離し、プログラムコードがよりエレガントであることを確認し、プログラムの堅牢性を向上させることができます。例外を効果的に使用する場合、例外はこれらの3つの質問に明確に答えることができます。例外タイプは「what」と答え、例外スタックトレースは「どこに「」と回答し、例外情報は「なぜ」と答えます。
Java例外メカニズムで使用されているいくつかのキーワード:試し、キャッチ、最後に、スロー、スロー。
| キーワード | 説明します |
|---|---|
| 試す | リスニングに使用されます。 Tryステートメントブロックに(例外をスローする可能性のあるコード)を聞くコードを表示します。 TRYステートメントブロックで例外が発生すると、例外がスローされます。 |
| キャッチ | 例外をキャッチするために使用されます。キャッチは、TRYステートメントブロックで発生する例外をキャッチするために使用されます。 |
| ついに | 最後に、ステートメントブロックは常に実行されます。主に、TRYブロックで開かれた材料リソース(データベース接続、ネットワーク接続、ディスクファイルなど)をリサイクルするために使用されます。最終的なブロックの実行後にのみ、TryまたはCatchブロックでステートメントを返したり、スローしたりします。リターンやスローなどの最終的なステートメントが使用されている場合、それらは実行に戻ることはなく、直接停止します。 |
| 投げる | 例外をスローするために使用されます。 |
| スロー | メソッド署名で使用されて、メソッドによってスローされる可能性のある例外を宣言します。 |
public class demo1 {public static void main(string [] args){try {int i = 10/0; system.out.println( "i ="+i); } catch(arithmeticexception e){system.out.println( "catch exception"); System.out.println( "e.getMessage():" + e.getMessage()); system.out.println( "e.toString():" + e.toString()); system.out.println( "e.printstacktrace():"); e.printstacktrace(); }}}実行結果:
exceptione.getMessage(): / by zeroe.tostring():java.alithmeticexception: / by zeroe.printstacktrace():java.lang.arithmeticexception: / by demo1.main(demo1.java:6)
結果の説明:TRYステートメントブロックに0の除数がある操作があり、操作によりjava.lang.arithmeticexceptionの例外がスローされます。キャッチによって、例外がキャッチされます。
結果を観察すると、system.out.println( "i ="+i)が実行されていないことがわかりました。これは、TRYステートメントブロックで例外が発生した後、TRYステートメントブロックの残りのコンテンツが実行されなくなることを意味します。
例2:最終的にの基本的な使用法を理解します
「例1」に基づいて、最終的にステートメントを追加します。
public class demo2 {public static void main(string [] args){try {int i = 10/0; system.out.println( "i ="+i); } catch(arithmeticexception e){system.out.println( "catch exception"); System.out.println( "e.getMessage():" + e.getMessage()); system.out.println( "e.toString():" + e.toString()); system.out.println( "e.printstacktrace():"); e.printstacktrace(); }最後に{system.out.println( "finally"); }}}実行結果:
exceptione.getMessage(): / by zeroe.tostring():java.lang.arithmeticexception: / by zeroe.printstacktrace():java.lang.arithmeticexception: / by demo2.main(demo2.java:6)
結果:最後にステートメントブロックが実行されました。
例3:スローとスローの基本的な使用法を理解する
スローはスローされた例外を宣言するために使用されますが、スローは例外をスローするために使用されます。
クラスMyExceptionは例外を拡張します{public MyException(){} public myException(string msg){super(msg); }} public class demo3 {public static void main(string [] args){try {test(); } catch(myexception e){system.out.println( "Catch My Exception"); e.printstacktrace(); }} public static void test()throws myException {try {int i = 10/0; system.out.println( "i ="+i); } catch(arithmeticexception e){throw new myexception( "これはmyexception"); }}}実行結果:
私の例外をキャッチしますメイエクセプト:これはdemo3.test(demo3.java:24)のmyexceptionです。
結果:MyExceptionは、例外から継承されたサブクラスです。 arithmeticexception例外(分割者は0)はtest()のtryステートメントブロックで生成され、例外はキャッチに巻き込まれます。その後、MyExceptionの例外がスローされます。 Main()メソッドは、Test()にスローされたmyExceptionをキャプチャします。
Java例外フレームワーク
Java例外アーキテクチャ図:
1。スロー可能
Throwableは、Java言語のすべてのエラーまたは例外のスーパークラスです。
Throwableには、エラーと例外の2つのサブクラスが含まれています。それらは通常、異常が発生したことを示すために使用されます。
Throwableには、スレッドが作成されたときにスタックを実行するスレッドのスナップショットが含まれています。 Stack Traceデータなどの情報を取得するために、printStackTrace()などのインターフェイスを提供します。
2。例外
例外とそのサブクラスは、合理的なアプリケーションがキャプチャしたい条件を指摘するスロー可能な形式です。
3。runtimeexception
RuntimeExceptionは、Java仮想マシンの通常の動作中に例外をスローする可能性のあるスーパークラスです。
コンパイラは、runtimeexceptionの例外をチェックしません。たとえば、除数がゼロの場合、Arithmeticexception例外がスローされます。 runtimeexceptionは、算術のスーパークラスです。コードにゼロの除数がある場合、「スロー宣言をスローしない」または「try ... catch ...」を介して処理されない場合にコンパイルすることもできます。これは、「コンパイラはruntimeexception例外をチェックしない」と私たちが言うことです!
コードがRuntimeExceptionの例外を生成する場合、コードを変更することで避ける必要があります。たとえば、除数がゼロの場合、コードを使用してこの状況を回避する必要があります!
4。エラー
例外と同様に、エラーはスロー可能なサブクラスでもあります。合理的なアプリケーションがキャッチしようとしてはならないという深刻な問題を示すために使用され、そのようなエラーのほとんどは例外的な条件です。
runtimeexceptionのように、コンパイラはエラーをチェックしません。
Javaは、スロー可能な構造を3つのタイプに分割します:チェックされた例外(チェックされた例外)、ランタイム例外(runtimeexception)、エラー(エラー)。
(1)ランタイム例外
定義:RuntimeExceptionとそのサブクラスは、ランタイム例外と呼ばれます。
機能:Javaコンパイラはチェックしません。つまり、このような例外がプログラムで発生する可能性がある場合、「スロー宣言をスローしない」または「トライキャッチステートメントに巻き込まれていない」場合、コンパイルされて渡されます。たとえば、除数がゼロのときに生成されたarithmeticexception例外、配列が境界が削減されているときに生成されたインデックスoutofboundsexception例外、フェイルフェイルメカニズムなどによって生成される同時モジオ化の例外はすべてランタイムの例外です。
Javaコンパイラはランタイムの例外をチェックしませんが、スローを宣言してスローすることも、トライキャッチを通してキャプチャすることもできます。
ランタイム例外が生成された場合、コードを変更することで回避する必要があります。たとえば、除数がゼロの場合、コードを使用してこの状況を回避する必要があります。
(2)例外を確認します
定義:例外クラス自体、および「ランタイム例外」を除く例外のサブクラスの他のサブクラスは、すべてチェックされた例外と見なされます。
機能:Javaコンパイラがチェックします。このような例外は、スローを通じて宣言され、スローされるか、トライキャッチを通してキャプチャされます。そうしないと、コンパイルできません。たとえば、CloneNotsuptedExceptionはチェックされた例外です。オブジェクトがclone()インターフェイスを介してクローン化され、オブジェクトの対応するクラスがクローン可能なインターフェイスを実装しない場合、cloneNotsupportedexceptionがスローされます。
チェックされている例外は通常、回復できます。
(3)エラー
定義:エラークラスとそのサブクラス。
機能:ランタイムの例外と同様に、コンパイラはエラーをチェックしません。
リソースが不十分な場合、制約の障害、または他のプログラムが実行し続けることができない他の条件がある場合にエラーが発生します。プログラム自体はこれらのエラーを修正できません。たとえば、VirtualMachineErrorはエラーです。
Java Conventionによると、新しいエラーサブクラスを実装すべきではありません!
上記の3つの構造については、例外またはエラーをスローする必要がありますか? 「効果的なJava」に記載されている推奨事項は次のとおりです。回復できる条件に対してチェックされた例外を使用し、プログラムエラーにランタイム例外を使用します。
例外処理に関するいくつかの提案
第1条:異常な状況についてのみ例外を使用します
推奨事項:例外は異常な条件にのみ使用されるべきであり、通常の制御フローには使用しないでください。
説明は、以下の2つのコードを比較して行われます。
コード1
{int i = 0; while(true){arr [i] = 0; i ++; }} catch(indexoutofboundsexception e){} code 2for(int i = 0; i <arr.length; i ++){arr [i] = 0;}両方のコードの目的は、ARR配列を反復し、配列内の各要素の値を0に設定することです。コード1は例外によって終了します。 3つの主な理由でコード1を使用しないでください。
例外メカニズムの元の設計は異常な状況向けであるため、パフォーマンスを最適化しようとするJVM実装はほとんどありません。したがって、例外の作成、投げ、キャッチのオーバーヘッドは高価です。
トライキャッチリターンにコードを入力すると、JVMが実行する可能性のある特定の特定の最適化を実装することができなくなります。
トラバース配列の標準パターンは冗長チェックにつながることはなく、一部の最新のJVM実装はそれらを最適化します。
実際、例外ベースのモードは標準モードよりもはるかに遅いです。テストコードは次のとおりです。
パブリッククラスAdvice1 {private static int [] arr = new int [] {1,2,3,4,5}; Private static int size = 10000; public static void main(string [] args){long s1 = system.currenttimemillis(); for(int i = 0; i <size; i ++)endbyrange(arr); long e1 = system.currenttimemillis(); system.out.println( "endbyrange time:"+(e1-s1)+"ms"); long s2 = system.currenttimemillis(); for(int i = 0; i <size; i ++)endbyexception(arr); long e2 = system.currenttimemillis(); System.out.println( "endbyException time:"+(e2-s2)+"ms"); } // arr arrayをトラバースします:private static void endbyexception(int [] arr){try {int i = 0; while(true){arr [i] = 0; i ++; //system.out.println("endbyrange:arr ["+i+"] = "+arr [i]); }} catch(indexoutofofboundsexception e){}} // arr arrayを転送:private static void endbyrange(int [] arr){for(int i = 0; i <arr.length; i ++){arr [i] = 0; //system.out.println("endbyException:arr ["+i+"] = "+arr [i]); }}}実行結果:
endbyrange時間:8msendbyException時間:16ms
結果は、例外を通過する速度が通常の方法で配列を通過するよりもはるかに遅いことを示しています!
第2条:回復可能な条件のためにチェックされた例外を使用し、プログラムエラーにランタイム例外を使用します。
| 異常な | 説明します |
|---|---|
| ランタイム例外 | RuntimeExceptionクラスとそのサブクラスは、ランタイム例外と呼ばれます。 |
| チェックされた例外 | 例外クラス自体と、「ランタイム例外」を除く他のサブクラスはすべてチェックされた例外です。 |
違いは、Javaコンパイラが「チェックされた例外」をチェックし、「ランタイムの例外」をチェックしないことです。
つまり、チェックされた例外のために、それはスローを通して宣言されてスローされるか、トライキャッチを通してキャプチャされます。そうしないと、コンパイルできません。ランタイムの例外のために、それらが「スローをスローしない」または「トライキャッチステートメントに巻き込まれていない」場合、それらは引き続き編集されて渡されます。もちろん、Javaコンパイラはランタイムの例外をチェックしませんが、スローを通じて例外を説明したり、トライキャッチでキャッチすることもできます。
rithmeticexception(たとえば、除数は0)、indexoutofofboundsexception(たとえば、境界のない配列)などはすべてランタイムの例外です。この例外のために、コードを変更することでそれを避ける必要があります。チェックされた例外のために、プログラムを復元して処理を実行できます。たとえば、ユーザーが十分な数の呼び出しを保存しないため、給与コールで電話をかけようとすると失敗すると仮定します。したがって、チェックされた例外をスローします。
第3条:チェックされた例外の不必要な使用を避けてください
「検閲された例外」は、Javaの良い機能です。リターンコードとは異なり、「例外をチェック」して、プログラマーに例外条件に対処するように強制され、プログラムの信頼性が大幅に向上します。
ただし、チェックされた例外を過度に使用すると、APIが非常に不便になる可能性があります。メソッドが1つ以上のチェックされた例外をスローする場合、メソッドを呼び出すコードは、これらの例外を1つ以上のCACTステートメントブロックで処理する必要があります。または、スロー宣言をスローする必要があります。それがキャッチによって処理されるか、スローをスローして宣言をスローするかにかかわらず、それはプログラマーに無視できない負担を追加します。
「チェックされた例外」のために2つの条件を満たす必要があります。まず、APIが正しく使用されていても、例外条件の発生を防ぐことはできません。第二に、例外が生成されると、APIを使用するプログラマーは、プログラムを処理するために有用なアクションを実行できます。
第4条:標準的な例外を使用してみてください
コードの再利用はアドボカシーに値するものであり、これは一般的なルールであり、例外も例外ではありません。既存の例外を再利用するには、いくつかの利点があります。
まず、プログラマーが慣れてきたイディオムと一致するため、APIが学習と使用を容易にします。
第二に、これらのAPIを使用するプログラムの場合、プログラマーに馴染みのない例外で満たされていないため、読みやすくなります。
第三に、例外クラスが少ないほど、メモリの使用量が小さくなり、これらのクラスの転載に費やす時間が小さくなります。
Java標準の例外のいくつかは、よく使用される例外です。次の表:
| 異常な | 機会を使用します |
|---|---|
| IllegalargumentException | パラメーター値は適切ではありません |
| IllegalStateException | パラメーターは不適切ではありません |
| nullpointerexception | nullが無効になっている場合、パラメーター値はnullです |
| indexoutofboundsexception | サブスクリプトは境界を越えます |
| concurrentModificationException | 同時変更が禁止されている場合、オブジェクトは同時の変更を検出します |
| UnsportedoperationException | オブジェクトが顧客の要求をサポートしない方法 |
これらはこれまでにJavaプラットフォームライブラリの最も一般的に再利用された例外ですが、他の例外もライセンス条件下で再利用することができます。たとえば、複雑な数値やマトリックスなどの算術オブジェクトを実装する場合は、算術XceptionとNumberformatexceptionを再利用することが非常に適切です。例外があなたのニーズを満たしている場合は、それを使用することをためらわないでください。ただし、例外をスローする条件が、例外のドキュメントで説明されている条件と一致することを確認する必要があります。この再利用は、名前ではなくセマンティクスに基づいている必要があります!
最後に、再利用する例外を選択する際に従わなければならないルールがないことを明確にしてください。たとえば、カードオブジェクトのケースを考慮すると、操作を処理する方法があり、そのパラメーター(Handize)が手に与えられるカードの数であると仮定します。このパラメーターの値をデッキ全体の残りのカードの数よりも大きいと仮定します。この状況は、違法な状況(Handsizeの値が大きすぎる)またはIllegalStateException(カードオブジェクトにはクライアントの要求に比べてカードが少なすぎます)として解釈できます。
第5条:スローされた例外は、対応する抽象化に適している必要があります
この状況は、メソッドによってスローされた例外が実行するタスクと明らかな相関関係がない場合、圧倒的です。これは、メソッドが低レベルの抽象化によってスローされた例外を渡すと、多くの場合発生します。これが起こると、混乱するだけでなく、高レベルのAPIも「汚染」します。
この問題を回避するために、高レベルの実装は低レベルの例外をキャッチし、高レベルの抽象化に従って導入できる例外をスローする必要があります。このプラクティスは「例外翻訳」と呼ばれます。
たとえば、Java Collection Framework Abstract fackentiallist get()メソッドは次のとおりです(JDK1.7.0_40に基づく):
public e get(int index){try {return listiterator(index).next(); } catch(nosuchelementexception exc){新しいindexOutofboundsexception( "index:"+index); }}ListIterator(index)は、Listiteratorオブジェクトを返します。オブジェクトの次()メソッドを呼び出すと、nosuchelementexception例外がスローされる場合があります。 get()メソッドでは、nosuchelementexceptionの例外をスローすることは混乱します。したがって、get()はnosuchelementexceptionをキャプチャし、IndexOutofofboundsexception例外をスローします。つまり、nosuchelementExceptionをIndexOutOutOutOutOutSexcectionの例外に変換することと同等です。
第6条:各方法でスローされる例外は文書化する必要があります
チェックされた例外を個別に宣言し、Javadocの@Throwsタグを使用して、スローされる各例外の条件を正確に記録します。
クラス内の多くの方法が同じ理由で同じ例外を投げる場合、各方法について個別にドキュメントするのではなく、このクラスのドキュメントコメントでこの例外のドキュメントを作成することは許容されます。
第7条:メッセージキャプチャメッセージを詳細に含める
要するに、例外をカスタマイズまたはスローする場合、障害に関連する情報を含める必要があります。
猛攻撃の例外のためにプログラムが故障した場合、システムは例外のスタックトレースを自動的に印刷します。スタックトラックに例外の文字列表現が含まれています。通常、例外クラスのクラス名と、次の詳細なメッセージが含まれます。
第8条:障害の原子性を維持するよう努めています
オブジェクトが例外をスローする場合、オブジェクトは明確に定義された利用可能な状態にあることを常に期待しています。これは、チェックされた例外にとって特に重要です。なぜなら、発信者は通常、チェックされた例外から回復することを期待しているためです。
一般的に言えば、失敗したメソッド呼び出しは、オブジェクトを「呼び出される前にその状態」に保つ必要があります。このような属性を持つ方法は、「故障原子」と呼ばれます。障害は依然として原子性を維持していることが理解できます。オブジェクトが「失敗した原子性」を維持する方法はいくつかあります。
(1)マット不可能なオブジェクトを設計します。
(2)可変オブジェクトで操作を実行する方法の場合、「失敗した原子性」を取得する最も一般的な方法は、操作を実行する前にパラメーターの妥当性を確認することです。次のように(stack.javaのPOPメソッド):
public object pop(){if(size == 0)throw new emptystackexception();オブジェクト結果=要素[ - サイズ];要素[size] = null; return result;} (3)以前の方法と同様に、計算処理を調整して、オブジェクト状態が変更される前に計算部分の障害が発生する可能性があるようにすることができます。
(4)操作中の障害を説明し、操作が開始される前にオブジェクトを状態に戻すようにするための回復コードを書きます。
(5)オブジェクトの一時コピーで操作を実行し、操作が完了したら、結果を元のオブジェクトにコピーします。
「オブジェクトの失敗した原子性を維持する」ことが望ましい目標ですが、常に可能であるとは限りません。たとえば、複数のスレッドが適切な同期メカニズムなしに同時にオブジェクトにアクセスしようとする場合、オブジェクトは一貫性のない状態に残される場合があります。
「失敗した原子性」を達成できる状況でさえ、それは常に予想されるとは限りません。一部の操作では、頭上や複雑さを大幅に増加させる可能性があります。
一般的なルールは次のとおりです。メソッド仕様の一部として、メソッドを呼び出す前にオブジェクトの状態を変更することは例外ではありません。このルールが違反されている場合、APIドキュメントは、オブジェクトがどのような状態にあるかを明確に示す必要があります。
第9条:例外を無視しないでください
APIデザイナーがメソッドが例外をスローすることを宣言すると、彼らは何かを説明しようとしています。だから、それを無視しないでください!例外を無視するコードは次のとおりです。
try {...} catch(somexceptione){}空のキャッチブロックは、例外が適切な目的を達成できないようにします。例外の目的は、異常な状態に対処することを強制することです。例外を無視することは、火災警報信号を無視するようなものです。火災警報信号がオフになっている場合、実際の火災が発生したときに誰も火災警報信号が表示されません。したがって、少なくともキャッチブロックには、この例外を無視することが適切である理由を説明する説明が含まれている必要があります。