1. 例外の種類
すべての例外は 4 つのタイプに分類され、実際には相互に継承するクラスです。
Throwable
クラス
すべての例外の基本クラスはクラスですThrowable
。このThrowable
クラスには、現在の呼び出しスタック (現在のメソッドのスタック トレース) を配列に書き込むコードが含まれています。スタック トレースとは何かについては、少し後で学びます。
throw演算子は、クラスから派生したオブジェクトのみを受け入れることができますThrowable
。理論的には のようなコードを書くこともできますがthrow new Throwable();
、通常は誰もこれを行いません。このクラスの主な目的はThrowable
、すべての例外に対して単一の親クラスを持つことです。
Error
クラス
次の例外クラスはError
、クラスを直接継承するクラスですThrowable
。重大な問題が発生した場合、 Java マシンはError
クラス (およびその子孫)のオブジェクトを作成します。たとえば、ハードウェアの故障、メモリ不足などです。
通常、プログラマーとして、このようなエラー (エラーがスローされるべき種類) がプログラム内で発生した状況では、何もすることができません。これらのエラーは深刻すぎるためです。Error
できることは、プログラムがクラッシュしていることをユーザーに通知するか、エラーに関する既知の情報をすべてプログラム ログに書き込むことだけです。
Exception
クラス
およびクラスは、多くのメソッドの操作中に発生する一般的なエラーに対応しますException
。スローされた各例外の目標は、それを適切に処理する方法を知っているブロックによってキャッチされることです。RuntimeException
catch
メソッドが何らかの理由で作業を完了できない場合、適切なタイプの例外をスローして呼び出し側メソッドに直ちに通知する必要があります。
つまり、変数が に等しい場合null
、メソッドは をスローしますNullPointerException
。不正な引数がメソッドに渡された場合、メソッドは をスローしますInvalidArgumentException
。メソッドが誤ってゼロで除算した場合、 がスローされますArithmeticException
。
RuntimeException
クラス
RuntimeExceptions
は のサブセットですExceptions
。RuntimeException
これは通常の例外 ( ) の軽量バージョンであるとさえ言えますException
。そのような例外に課される要件と制限は少なくなります。
Exception
との違いについては後で学びますRuntimeException
。
2. Throws
:チェックされた例外
すべての Java 例外は、「チェック済み」と「チェックなし」の2 つのカテゴリに分類されます。
RuntimeException
またはを継承するすべての例外は、未チェック例外Error
とみなされます。 その他はすべてチェック例外です。
チェック例外が導入されてから 20 年が経過しましたが、ほとんどすべての Java プログラマーがこれをバグだと考えています。一般的な最新のフレームワークでは、すべての例外の 95% がチェックされていません。Java をほぼ正確にコピーした C# 言語は、チェック例外を追加しませんでした。
チェック例外とチェックなし例外の主な違いは何ですか?
チェック例外には追加の要件が課されます。大まかに言うと、これらは次のとおりです。
要件1
メソッドがチェック例外をスローする場合、そのシグネチャで例外のタイプを示す必要があります。そうすることで、それを呼び出すすべてのメソッドは、この「意味のある例外」がその中で発生する可能性があることを認識します。
キーワードの後のメソッド パラメータの後にチェック例外を示します(キーワードを誤って使用しないでください)。次のようになります。throws
throw
type method (parameters) throws exception
例:
チェックされた例外 | 未チェックの例外 |
---|---|
|
|
右側の例では、コードは未チェックの例外をスローします。追加のアクションは必要ありません。 左側の例では、メソッドがチェック済み例外をスローするため、throws
例外のタイプとともにキーワードがメソッド シグネチャに追加されます。
メソッドが複数のチェック例外をスローすることを予期している場合は、それらすべてをthrows
キーワードの後にカンマで区切って指定する必要があります。順序は重要ではありません。例:
public void calculate(int n) throws Exception, IOException
{
if (n == 0)
throw new Exception("n is null!");
if (n == 1)
throw new IOException("n is 1");
}
要件2
シグネチャ内にチェック例外が含まれるメソッドを呼び出す場合、例外がスローされるという事実を無視することはできません。
catch
このような例外をすべてキャッチするには、例外ごとにブロックを追加するか、メソッドの句にブロックを追加する必要があります。throws
それはまるで、「これらの例外は非常に重要なので、必ずキャッチしなければなりません。そして、それらの処理方法がわからない場合、メソッドを呼び出す可能性のある人には、そのような例外が発生する可能性があることを通知する必要があります。」と言っているかのようです。
例:
人間が住む世界を作成するメソッドを作成していると想像してください。初期人数は引数として渡されます。したがって、人数が少なすぎる場合は例外を追加する必要があります。
地球を創る | ノート |
---|---|
|
このメソッドは、次の 2 つのチェック例外をスローする可能性があります。
|
このメソッド呼び出しは 3 つの方法で処理できます。
1. 例外をキャッチしない
これは、メソッドが状況を適切に処理する方法を知らない場合に最も頻繁に行われます。
コード | ノート |
---|---|
|
呼び出し側メソッドは例外をキャッチしないため、例外を他のメソッドに通知する必要があります。例外を独自のthrows 句に追加します。 |
2. いくつかの例外をキャッチする
対応できるエラーには対応いたします。しかし、理解できないものは、呼び出し側のメソッドにスローします。これを行うには、throws 節に名前を追加する必要があります。
コード | ノート |
---|---|
|
呼び出し元は、チェックされた例外を 1 つだけキャッチします — LonelyWorldException 。他の例外は、その署名にthrows キーワードの後にそれを示すように追加する必要があります。 |
3. すべての例外をキャッチする
メソッドが呼び出し側メソッドに例外をスローしない場合、呼び出し側メソッドは常にすべてが正常に動作したと確信します。そして、例外的な状況を修正するための措置を講じることができなくなります。
コード | ノート |
---|---|
|
すべての例外はこのメソッドでキャッチされます。電話をかけてきた人は、すべてがうまくいったと確信するでしょう。 |
3. ラッピング例外
チェック例外は理論的にはクールに見えましたが、実際には大きなフラストレーションとなることが判明しました。
プロジェクトに非常に人気のあるメソッドがあるとします。これはプログラム内の何百もの場所から呼び出されます。そして、新しいチェック例外をそれに追加することにしました。そして、このチェック例外は非常に重要で特別なので、それがキャッチされた場合に何をすべきかをメソッドだけが知っている可能性がありますmain()
。
つまり、非常に人気のあるメソッドを呼び出すすべてのメソッドの句にチェック例外を追加する必要がありますthrows
。同様に、throws
それらのメソッドを呼び出すすべてのメソッドの句でも同様です。また、それらのメソッドを呼び出すメソッドについても同様です。
その結果、throws
プロジェクト内のメソッドの半分の句に新しいチェック例外が発生します。そしてもちろん、プロジェクトはテストの対象になっていますが、テストはコンパイルされません。そして今度は、テスト内の throws 句も編集する必要があります。
そして、すべてのコード (数百のファイルのすべての変更) を他のプログラマーがレビューする必要があります。そしてこの時点で、なぜプロジェクトにこれほど多くの血なまぐさい変更を加えたのかを自問します。数日の作業と壊れたテスト - すべては 1 つのチェック例外を追加するためでしょうか?
そしてもちろん、継承とメソッドのオーバーライドに関連する問題はまだ残っています。チェック例外によって生じる問題は、利点よりもはるかに大きいです。肝心なのは、今ではそれらを愛する人も使用する人もほとんどいないということです。
ただし、これらのチェック例外を含むコード (標準 Java ライブラリ コードを含む) が依然として多数存在します。彼らに対して何をすべきでしょうか?私たちはそれらを無視することはできませんし、どのように対処すればよいのかわかりません。
Java プログラマーは、チェックされた例外を でラップすることを提案しましたRuntimeException
。つまり、すべてのチェック済み例外をキャッチしてから、非チェック例外 (たとえば、RuntimeException
) を作成し、代わりにそれらをスローします。これを実行すると次のようになります。
try
{
// Code where a checked exception might occur
}
catch(Exception exp)
{
throw new RuntimeException(exp);
}
これはあまりきれいな解決策ではありませんが、ここには何も犯罪的なものはありません。例外は単に .html の中に詰め込まれているだけですRuntimeException
。
必要に応じて、そこから簡単に取得できます。例:
コード | ノート |
---|---|
|
オブジェクト内に格納されている例外を取得します RuntimeException 。変数は、 その型を決定し、それをチェックcause 例外型に変換する可能性があります。 null |
4. 複数の例外をキャッチする
プログラマーはコードを複製することを非常に嫌います。彼らは、対応する開発原則「 Don't Reply Yourself (DRY)」も考案しました。ただし、例外を処理する場合、1 つのブロックの後に同じコードを持つ複数のブロックが続くことがよくあります。try
catch
catch
または、同じコードを持つ3 つのブロックと、別の同じコードを持つ別の 2 つのブロックが存在する可能性がありますcatch
。これは、プロジェクトが責任を持って例外を処理する場合の標準的な状況です。
バージョン 7 以降、Java 言語には、単一のcatch
ブロックで複数のタイプの例外を指定する機能が追加されました。次のようになります。
try
{
// Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
// Exception handling code
}
catch
ブロックは必要な数だけ持つことができます。ただし、単一のcatch
ブロックで、相互に継承する例外を指定することはできません。つまり、クラスは を継承するため、catch ( Exception
| RuntimeException
e)を記述することはできません。RuntimeException
Exception
5. カスタム例外
いつでも独自の例外クラスを作成できます。クラスを継承するクラスを作成するだけですRuntimeException
。次のようになります。
class ClassName extends RuntimeException
{
}
詳細については、OOP、継承、コンストラクター、メソッドのオーバーライドを学習する際に説明します。
ただし、このような単純なクラスしかない場合でも (コードがまったくない場合)、それに基づいて例外をスローすることができます。
コード | ノート |
---|---|
|
チェックされていない を スローします MyException 。 |
Java マルチスレッドのクエストでは、独自のカスタム例外の操作について詳しく説明します。
GO TO FULL VERSION