プログラムを完璧にすることは難しく、必ずさまざまな異常が生じます。たとえば、プログラムの印刷時にプリンターの用紙がなくなったり、メモリが不足したりするなど、プログラム自体にバグがあります。これらの例外を解決するには、例外が発生した理由を知る必要があります。いくつかの一般的な例外については、特定の対応計画を提供することもできます。 C 言語の例外処理は関数の戻り値によって単純に実装されますが、戻り値の意味は規約によって決定されることがよくあります。プログラマは、あいまいな理由を見つける前に、大量の情報をクエリする必要があります。 C++、Java、Python などのオブジェクト指向言語には、より複雑な例外処理メカニズムがあることがよくあります。ここでは、Java の例外処理メカニズムについて説明します。
Java例外処理
例外処理
Java の例外処理メカニズムの大部分は C++ から来ています。これにより、プログラマは一時的に処理できない問題をスキップしてその後の開発を続行したり、プログラムがよりインテリジェントに例外を処理したりできるようになります。
Java は異常な状態を表すためにいくつかの特別なオブジェクトを使用します。そのようなオブジェクトは例外オブジェクトと呼ばれます。例外が発生すると、Java は事前設定に基づいて現在の状況を表すオブジェクトをスローします。いわゆる投げは特殊な返し方です。スレッドは、例外ハンドラーが発生するまで、レイヤーごとに一時停止してメソッド呼び出しを終了します。例外ハンドラーは例外オブジェクトをキャッチし、そのオブジェクトに基づいて次のアクションを決定します。
例外を処理してプログラムを続行し、プログラムを終了するようユーザーに通知します。
...
例外ハンドラーは次のようになり、try、catch、finally およびそれに続くブロックで構成されます。最後に必要ありません。
try { ...;}catch() { ...;}catch() { ...;}最後に { ...;}
この例外ハンドラーは、try に続くプログラム ブロックを監視します。 catch の括弧には、キャッチする例外のタイプを表すパラメータがあります。 catch は、対応する型とその派生クラスをキャプチャします。 try の後のプログラム ブロックには、例外タイプに対して実行される操作が含まれています。 try によって監視されるプログラム ブロックは複数のタイプの例外をスローする可能性があるため、例外ハンドラーは複数の catch モジュールを持つことができます。 final 以降のプログラムブロックは、例外が発生するかどうかに関わらず必ず実行しなければならないプログラムです。
問題が発生する可能性があり、監視する必要があるプログラムを try に置き、例外に対処するソリューションを catch に設計します。
以下は、例外処理を使用する Java プログラムの一部です。プログラムの try 部分は、ファイルからテキスト行を読み取ります。ファイルの読み取りプロセス中に、IOException が発生する場合があります。
BufferedReader br = new BufferedReader(new FileReader("file.txt"));try { StringBuilder sb = new StringBuilder(); String line = br.readLine(); while (line != null) { sb.append(line) ; sb.append("/n"); 行 = br.readLine() } sb.toString();} catch(IOException e) { e.printStackTrace(); System.out.println("IO 問題");}finally { br.close();}
IOException クラス オブジェクト e をキャッチすると、そのオブジェクトを操作できます。たとえば、オブジェクトの printStackTrace() を呼び出して、現在のスタック ステータスを出力します。さらに、ミッドレンジには「IO 問題」というプロンプトも出力しました。
例外があるかどうかに関係なく、プログラムは最終的にfinallyブロックに入ります。 finally ブロックでファイルを閉じ、ファイル記述子が占有しているリソースをクリアします。
例外の種類
Java の例外クラスはすべて Trowable クラスから継承します。 Throwableクラスのオブジェクトは投げることができます(throw)。
オレンジ: チェックなし、青: チェックあり
投げられるオブジェクトは 2 つのグループに分類できます。 1 つのグループはチェックされていない例外であり、このグループの例外には例外処理メカニズムが使用されないことがよくあります。
1. Error クラスは通常、Java の内部エラーやリソース枯渇などのエラーを指します。エラー (およびその派生) が発生した場合、プログラミング レベルでエラーを解決することはできないため、プログラムを直接終了する必要があります。
2. Exception クラスには特別な派生クラス RuntimeException があります。 RuntimeException (およびその派生現象) は、Java プログラム自体、つまりプログラマがプログラミング中にミスをしたことが原因で発生します。 RuntimeException は Java プログラムを変更することで完全に回避できます。たとえば、継承関係を持たずに、ある型のオブジェクトを別の型に変換すると、ClassCastException が発生します。このような異常は回避する必要があり、回避することができます。
残りはチェックされた例外です。これらのクラスは、実行時にプログラム内でエラーを引き起こす環境とのプログラミングの相互作用によって発生します。たとえば、ファイルを読み取るときに、ファイル自体のエラーにより IOException が発生します。もう 1 つの例は、ネットワーク サーバーが URL ポイントを一時的に変更し、MalformedURLException を引き起こすことです。ファイル システムとネットワーク サーバーは Java 環境の外部にあり、プログラマの制御範囲内にはありません。プログラマが例外を予測できる場合、例外処理メカニズムを使用して対応計画を作成できます。たとえば、ファイルに問題がある場合、システム管理者に警告が送信されます。もう 1 つの例は、ネットワーク サーバーに問題が発生した場合、ユーザーに通知され、ネットワーク サーバーが回復するまで待つことです。例外処理メカニズムは、主にこのような例外を処理するために使用されます。
例外をスローする
上記のプログラムでは、例外は Java IO API の呼び出しから発生します。また、充電と使用方法を備えた次のバッテリー クラスなど、独自のプログラムで例外をスローすることもできます。
public class Test{ public static void main(String[] args) { Battery aBattery = new Battery(); aBattery.chargeBattery(0.5); aBattery.useBattery(-0.5) }} class Battery { /** * バッテリーを増やす * / public void ChargeBattery(double p) { // power <= 1 if (this.power + p < 1.) { this.power = this.power + p; } else { this.power = 1.; } } /** * バッテリーを消費します */ public boolean useBattery(double p) { try { test(p) } catch(Exception e) { out.println("catch Exception"); System.out.println(e.getMessage()); p = 0.0; } if (this.power >= p) { this.power = this.power - p; return true; } else { this.power = 0.0; return false; } /** * テストの使用方法 */ private void test(double p) throws 例外をスローします。 p < 0) { Exception e = new Exception("p は正でなければなりません"); } private double power = 0.0;
useBattery() はバッテリー動作の使用を示します。 useBattery() メソッドには、使用される電力量を示すパラメータがあります。このパラメータをテストするには test() メソッドを使用します。このパラメータが負の場合、例外があると考えられ、例外がスローされます。
テストでは、例外が発生した場合 (p < 0)、Exception オブジェクト e を作成し、文字列をパラメーターとして使用します。文字列には例外関連の情報が含まれており、このパラメータは必須ではありません。 Exception オブジェクトをスローするには throw を使用します。
useBattery() には例外ハンドラーがあります。 test() メソッドは生成した例外を直接処理するのではなく、上位の useBattery() に例外をスローするため、test() の定義でスロー例外について説明する必要があります。
(例外ハンドラーが useBattery() 内ではなく、上位レベルの main() メソッド内にあると仮定すると、 useBattery() の定義に throws Exception を追加する必要もあります。)
catch では、getMessage() メソッドを使用して、例外に含まれる情報を抽出します。上記のプログラムを実行した結果は次のようになります。
catch Exceptionp は正の値でなければなりません
例外ハンドラーでは、Exception クラスまたはその派生クラスの例外をキャッチします。これは、特にプログラムが複数の例外をスローする可能性がある場合、問題を特定するのに役に立たないことがよくあります。キャプチャするより具体的なクラスを提供できます。
カスタム例外
継承を通じて新しい例外クラスを作成できます。継承する場合、多くの場合、コンストラクターをオーバーライドする必要があります。例外には 2 つのコンストラクターがあり、1 つはパラメーターを持たず、もう 1 つは文字列パラメーターを持ちます。例えば:
class BatteryUsageException extends Exception{ public BatteryUsageException() {} public BatteryUsageException(String msg) { super(msg) }}
派生クラスでは、より多くの例外関連のメソッドと情報を提供できます。
例外をカスタマイズするときは、どの基本クラスを継承するかに注意してください。より具体的なクラスには、IOException と Exception など、より多くの例外情報が含まれる必要があります。
要約する
例外処理は問題を解決しますが、同時に問題を引き起こします。大規模なプロジェクトでは、過剰かつ詳細な例外処理により、プログラムが混乱することがよくあります。例外処理は設計上単純ではないため、使用には注意が必要です。