web-dev-qa-db-ja.com

Java:チェック済みと未チェックの例外の説明

私はStackOverFlowで、チェックされた例外とチェックされていない例外について複数の記事を読みました。私は正直なところまだそれらを正しく使用する方法についてはかなりよくわからない。

Joshua Blochの "Effective Java"は

回復可能な条件にはチェック済みの例外を使用し、プログラミングエラーには実行時の例外を使用します(第2版の項目58)

私がこれを正しく理解しているかどうか見てみましょう。

これがチェック済み例外の理解です。

try{
    String userInput = //read in user input
    Long id = Long.parseLong(userInput);
}catch(NumberFormatException e){
    id = 0; //recover the situation by setting the id to 0
}

1.上記はチェック例外と見なされますか。

2. RuntimeExceptionは未チェックの例外ですか?

これが未チェックの例外についての私の理解です。

try{
    File file = new File("my/file/path");
    FileInputStream fis = new FileInputStream(file);   
}catch(FileNotFoundException e){

//3. What should I do here?
    //Should I "throw new FileNotFoundException("File not found");"?
    //Should I log?
    //Or should I System.exit(0);?
}

4.さて、上記のコードもチェックされた例外ではないでしょうか。私はこのような状況を回復しようとすることができますか?できますか? (注:私の3番目の質問は上のcatchの中です)

try{
    String filePath = //read in from user input file path
    File file = new File(filePath);
    FileInputStream fis = new FileInputStream(file);   
}catch(FileNotFoundException e){
    //Kindly Prompt the user an error message
    //Somehow ask the user to re-enter the file path.
}

5.なぜ人々はこれをするのですか?

public void someMethod throws Exception{

}

なぜ彼らは例外を発生させるのですか?エラーをより早く処理していませんか?なぜバブルアップ?

編集:正確な例外をバブルアップするか、例外を使ってマスクするか?

以下は私の測定値です

Javaでは、チェック例外をいつ作成し、ランタイム例外にする必要がありますか。

チェックされた例外とチェックされていない例外を選択する場合

645
Thang Pham

多くの人々は、チェックされた例外(つまり、明示的にキャッチまたは再スローするべき例外)はまったく使用すべきではないと言います。たとえば、C#では排除されていましたが、ほとんどの言語にはありません。だからあなたは常にRuntimeExceptionのサブクラスを投げることができます(未チェックの例外)

しかし、私はチェック例外が有用であると思います - それらはあなたがあなたのAPIのユーザーに例外的な状況を処理する方法を考えさせたいときに使われます(それが回復可能なら)。 Javaプラットフォームでチェック済み例外が過剰に使用されているため、人々はそれらを嫌いです。

これが私のトピックに関する私の拡張見解です

特定の質問に関しては:

  1. NumberFormatExceptionはチェック例外を考慮していますか?
    いいえ。 NumberFormatExceptionはチェックされていません(=はRuntimeExceptionのサブクラスです)。どうして?知りません。 (しかしisValidInteger(..)というメソッドがあったはずです)

  2. RuntimeExceptionは未チェックの例外ですか?
    はい、正確に。

  3. ここで何をすればいいの?
    このコードがどこにあるのか、そして何をしたいのかによって異なります。それがUIレイヤにある場合 - それをキャッチして警告を表示します。それがサービス層にあるならば - まったくそれをつかまえないで - それを泡立たせましょう。ただ例外を飲み込まないでください。ほとんどの場合に例外が発生した場合は、次のいずれかを選択してください。

    • それを記録して戻る
    • それを再投げる(メソッドによって投げられることを宣言する)
    • 現在の例外をコンストラクタに渡して新しい例外を構築します。
  4. それでは、上記のコードもチェックされた例外にはなり得ないでしょうか。私はこのような状況を回復しようとすることができますか?できますか?
    そうかもしれません。しかし、未チェックの例外をキャッチするのを妨げるものは何もありません。

  5. どうして人々はthrows節にクラスExceptionを追加するのですか?
    ほとんどの場合、人々は何をキャッチし、何を再スローするかを検討するのが面倒です。 Exceptionを投げることは悪い習慣であり、避けるべきです。

残念ながら、いつキャッチするのか、いつリスローするのか、いつチェックするのか、そしていつチェックしない例外を使うのかを決定するための単一の規則はありません。私はこれが多くの混乱と多くの悪いコードを引き起こすことに同意します。一般原則はBlochによって述べられています(あなたはその一部を引用しました)。そして一般的な原則はあなたがそれを扱うことができる層に例外を再投げることです。

431
Bozho

何かが「チェックされた例外」であるかどうかは、それをキャッチしたかどうか、またはcatchブロックで行ったこととは関係ありません。これは例外クラスの特性です。 Exceptionおよびそのサブクラスに対するRuntimeExceptionexceptのサブクラスであるものはすべて、チェック済み例外です。

Javaコンパイラは、チェック済みの例外をキャッチするか、メソッドのシグネチャで宣言するように強制します。それはプログラムの安全性を向上させることになっていました、しかし大多数の意見はそれがそれが作り出すデザイン問題の価値がないということであるようです。

なぜ彼らは例外を発生させるのですか?ハンドルエラーが早ければ早いほどよいでしょうか。なぜバブルアップ?

それが例外のpoint全体なのです。この可能性がなければ、例外は必要ないでしょう。それらを使用すると、エラーが最初に発生した低レベルの方法でエラーを処理するように強制するのではなく、選択したレベルでエラーを処理できます。

220
  1. 上記はチェック例外と見なされますか?いいえ例外を処理しているという事実は、それがRuntimeExceptionである場合、それをChecked Exceptionにはしません。

  2. RuntimeExceptionunchecked exceptionですか?はい

Checked ExceptionsJava.lang.ExceptionsubclassesですUnchecked ExceptionsJava.lang.RuntimeExceptionsubclassesです

チェック済み例外をスローする呼び出しは、try {}ブロックで囲むか、メソッドの呼び出し側で上のレベルで処理する必要があります。その場合、現在のメソッドは、呼び出し側が例外を処理するために適切な手配を行えるように、前述の例外をスローすることを宣言する必要があります。

お役に立てれば。

Q:正確な例外をバブルアップするべきですか、それともExceptionを使ってマスクするべきですか?

A:はい、これは非常に良い質問であり、設計上の重要な考慮事項です。 Exceptionクラスは非常に一般的な例外クラスで、内部の低レベルの例外をラップするために使用できます。カスタム例外を作成してその中にラップするほうがよいでしょう。しかし、そして大きなこと - 根本的な元の根本的な原因が不明瞭になることは決してありません。例えばDon't everは以下のようにします -

try {
     attemptLogin(userCredentials);
} catch (SQLException sqle) {
     throw new LoginFailureException("Cannot login!!"); //<-- Eat away original root cause, thus obscuring underlying problem.
}

代わりに次のようにしてください。

try {
     attemptLogin(userCredentials);
} catch (SQLException sqle) {
     throw new LoginFailureException(sqle); //<-- Wrap original exception to pass on root cause upstairs!.
}

元の根本的な原因を食い払うことは、回復を超えた実際の原因を埋めることですが、彼らがアクセスできるのはアプリケーションログとエラーメッセージだけである生産サポートチームにとっては悪夢です。後者はより良い設計ですが、開発者が基本的なメッセージを発信者に渡さないため、多くの人が使用することはあまりありません。そのため、Always pass on the actual exceptionはアプリケーション固有の例外にラップされているかどうかにかかわらず戻ってきます。

試してみるときRuntimeExceptions

RuntimeExceptionsは原則として試してはいけません。それらは一般的にプログラミングエラーを示しており、そのままにしておくべきです。代わりに、プログラマはRuntimeExceptionという結果になる可能性があるコードを呼び出す前にエラー状態を確認する必要があります。例:

try {
    setStatusMessage("Hello Mr. " + userObject.getName() + ", Welcome to my site!);
} catch (NullPointerException npe) {
   sendError("Sorry, your userObject was null. Please contact customer care.");
}

これは悪いプログラミング方法です。代わりに、nullチェックが次のように行われているはずです。

if (userObject != null) {
    setStatusMessage("Hello Mr. " + userObject.getName() + ", Welome to my site!);
} else {
   sendError("Sorry, your userObject was null. Please contact customer care.");
}

しかし、そのようなエラーチェックが数値フォーマットのように高価である場合があります、これを考慮してください -

try {
    String userAge = (String)request.getParameter("age");
    userObject.setAge(Integer.parseInt(strUserAge));
} catch (NumberFormatException npe) {
   sendError("Sorry, Age is supposed to be an Integer. Please try again.");
}

ここで呼び出し前のエラーチェックは、parseInt()メソッド内ですべての文字列から整数への変換コードを複製することを基本的に意味し、開発者が実装した場合はエラーが発生しやすいので、努力する価値はありません。だからtry-catchをやめたほうがいいです。

したがって、NullPointerExceptionNumberFormatExceptionはどちらもRuntimeExceptionsなので、エラーが発生しやすいコードの導入を避けるためにNullPointerExceptionを明示的にキャッチすることをお勧めしますが、NumberFormatExceptionをキャッチすることはグレースフルNULLチェックに置き換える必要があります。

64
d-live

1例外について不明な場合は、APIを確認してください。

 Java.lang.Object
 extended by Java.lang.Throwable
  extended by Java.lang.Exception
   extended by Java.lang.RuntimeException  //<-NumberFormatException is a RuntimeException  
    extended by Java.lang.IllegalArgumentException
     extended by Java.lang.NumberFormatException

2。はい、それを拡張するすべての例外。

3。同じ例外をキャッチしてスローする必要はありません。この場合、新しいファイルダイアログを表示できます。

4。 FileNotFoundExceptionisはすでにチェック済みの例外です。

5。メソッドがsomeMethodを呼び出して例外をキャッチすると予想される場合、例外をスローできます。 「ボールを渡す」だけです。使用例は、独自のプライベートメソッドでスローし、代わりにパブリックメソッドで例外を処理する場合です。

良い読み物は、Oracleドキュメント自体です。 http://download.Oracle.com/javase/tutorial/essential/exceptions/runtime.html

設計者が、スコープ内でスローできるキャッチされていないチェック例外をすべて指定するようにメソッドを強制することにしたのはなぜですか?メソッドがスローできる例外は、メソッドのパブリックプログラミングインターフェイスの一部です。メソッドを呼び出す人は、メソッドがスローできる例外について知っておく必要があります。そうすることで、それらのアクションを決定できます。これらの例外は、そのメソッドのプログラミングインターフェイスのパラメーターおよび戻り値と同じくらいの部分です。

次の質問は、「スローできる例外を含め、メソッドのAPIをドキュメント化するのが非常に良い場合、実行時例外も指定しないのはなぜですか?」ランタイム例外は、プログラミングの問題の結果である問題を表します。そのため、APIクライアントコードは、それらから回復したり、何らかの方法で処理したりすることを合理的に期待できません。このような問題には、ゼロ除算などの算術例外が含まれます。 NULL参照を介してオブジェクトにアクセスしようとするなどのポインター例外。インデックス付けの例外(大きすぎるまたは小さすぎるインデックスを介して配列要素にアクセスしようとするなど)。

Java言語仕様 にも重要な情報があります:

throws句で指定されたチェック済み例外クラスは、実装者とメソッドまたはコンストラクターのユーザーとの間のコントラクトの一部です

要点は、canRuntimeExceptionをキャッチできますが、必須ではなく、実際には実装は同じ非チェックを維持する必要がないということです例外はスローされます。これらは契約の一部ではありません。

18
Aleadam

1)いいえ、NumberFormatExceptionは未チェックの例外です。あなたがそれをチェックしていなくても(あなたはそれが必要ではありません)それがチェックされていないので。これはIllegalArgumentExceptionのサブクラスであるRuntimeExceptionのサブクラスだからです。

2)RuntimeExceptionは、チェックされていないすべての例外の根本です。 RuntimeExceptionのすべてのサブクラスはチェックされていません。エラー(Throwableの下に来る)を除いて、他の全ての例外とThrowableはチェックされます。

3/4)存在しないファイルを選んだことをユーザーに知らせて新しいファイルを要求することができます。または単に無効なものを入力したことをユーザーに通知するのをやめるだけです。

5)'Exception'を投げて捕まえるのは悪い習慣です。しかし、より一般的には、呼び出し側がそれに対処する方法を決定できるように、他の例外をスローすることがあります。たとえば、ファイル入力の読み込みを処理するライブラリを作成し、そのメソッドに存在しないファイルが渡された場合、その処理方法はわかりません。発信者は再度質問しますか、それとも終了しますか?それで、あなたは例外を呼び出し元にチェーンの上位に投げます。

多くの場合、プログラマが入力を検証しなかったためにunchecked Exceptionが発生します(最初の質問のNumberFormatExceptionの場合)。そういうわけでそれらをキャッチすることはオプションです、なぜならそれらの例外を生成することを避けるためのよりエレガントな方法があるからです。

9
dontocsata

チェックあり - 起こりがちです。コンパイル時にチェックイン。

例えば.. FileOperations

未チェック - データが不正なため。実行時にチェックインします。

例えば..

String s = "abc";
Object o = s;
Integer i = (Integer) o;

Exception in thread "main" Java.lang.ClassCastException: Java.lang.String cannot be cast to Java.lang.Integer
    at Sample.main(Sample.Java:9)

ここでの例外は悪いデータによるものであり、決してコンパイル時に判断することはできません。

7
bharanitharan

チェックされた例外はコンパイル時にJVMとそれに関連するリソース(ファイル/ db/stream/socketなど)によってチェックされます。チェック例外の動機は、コンパイル時にリソースが利用できない場合、アプリケーションはこれをcatch/finallyブロックで処理するための代替動作を定義する必要があるということです。

未チェックの例外は、純粋にプログラム上のエラー、誤った計算、nullデータ、またはビジネスロジックの失敗であっても、ランタイム例外につながる可能性があります。コード内の未チェックの例外を処理またはキャッチするのは絶対に問題ありません。

http://coder2design.com/Java-interview-questions/ からの説明

5
Jatinder Pal

ランタイム例外 ランタイム例外は、未チェック例外と呼ばれます。他の例外はすべてチェック済み例外であり、Java.lang.RuntimeExceptionから派生したものではありません。

チェックされた例外 チェックされた例外は、コードのどこかで捕捉されなければなりません。チェック済み例外をスローするメソッドを呼び出しても、チェック済み例外をどこかでキャッチしないと、コードはコンパイルされません。そのため、それらはチェック例外と呼ばれています。コンパイラは、それらが処理または宣言されていることを確認するためにチェックします。

Java APIのいくつかのメソッドはチェック済み例外をスローします。そのため、あなたが書かなかったメソッドによって生成された例外に対処するために例外ハンドラを書くことがよくあります。

3
Omer

私の 絶対的なお気に入り 未チェック例外とチェック済み例外の違いについての説明は、Javaチュートリアルチュートリアルの記事「 未チェックの例外 - 論争 」で提供されています。こんにちは、基本は時々最高です):

要点は次のとおりです。クライアントが例外からの回復を合理的に予想できる場合は、それをチェック済み例外にします。クライアントが例外から回復するために何もできない場合は、未チェックの例外にします。

「どのタイプの例外をスローするか」の中心は(ある程度)意味的であり、上記の引用は優れたガイドラインを提供します(したがって、特にLiskovが主張するようにC#がチェック済み例外を取り除いたという考えに私はまだ戸惑います)。それらの有用性).

残りは論理的になります。コンパイラが明示的に応答することを期待する例外はどれですか。あなたがクライアントから回復することを期待しているもの。

3
Thomas

最終的な質問に答えるには(他の人も上記で徹底的に答えているようです)、「私は正確な例外をバブルアップするべきですか、それともExceptionを使ってそれを隠すべきですか?」

私はあなたがこのような何かを意味すると仮定しています:

public void myMethod() throws Exception {
    // ... something that throws FileNotFoundException ...
}

いいえ、常に 最も正確な 可能な例外、またはそのようなリストを宣言してください。あなたがあなたのメソッドをスローできると宣言した例外はあなたのメソッドと呼び出し側の間の契約の一部です。 "FileNotFoundException"を投げることは、ファイル名が無効であり、ファイルが見つからない可能性があることを意味します。呼び出し側はそれをインテリジェントに処理する必要があります。 Exceptionを投げることは、「ちょっと、sh * tが起こります。取り引きする」を意味します。これは非常に貧弱なAPIです。

最初の記事のコメントには、 "throws Exception"が有効かつ妥当な宣言であるという例がいくつかありますが、これはあなたが書くことになるほとんどの "normal"コードには当てはまりません。

2
Tom Dibble

チェック例外をまったく使用しない理由をいくつか追加したいだけです。これは完全な答えではありませんが、私はそれがあなたの質問の一部に答え、そして他の多くの答えを補完すると感じます。

チェック例外が関係しているときはいつでも、メソッドシグネチャのどこかにthrows CheckedExceptionがあります(CheckedExceptionはどんなチェック例外でも構いません)。シグニチャは例外をスローしません、例外をスローすることは実装の側面です。インタフェース、メソッドシグネチャ、親クラス、これらすべてはそれらの実装に依存してはいけません。ここでチェックされた例外の使い方(実際にはメソッドシグネチャでthrowsを宣言しなければならないという事実)は、上位レベルのインタフェースをこれらのインタフェースの実装と結び付けることです。

例を挙げましょう。

このようなきれいできれいなインターフェースを作りましょう

public interface IFoo {
    public void foo();
}

これで、メソッドfoo()の多くの実装を書くことができます。

public class Foo implements IFoo {
    @Override
    public void foo() {
        System.out.println("I don't throw and exception");
    }
}

クラスFooは完璧です。それでは、Barクラスで最初の試みをしましょう。

public class Bar implements IFoo {
    @Override
    public void foo() {
        //I'm using InterruptedExcepton because you probably heard about it somewhere. It's a checked exception. Any checked exception will work the same.
        throw new InterruptedException();
    }
}

このクラスBarはコンパイルされません。 InterruptedExceptionはチェック済みの例外なので、それをキャプチャするか(メソッドfoo()内でtry-catchを使用して)宣言する必要があります(メソッドシグネチャにthrows InterruptedExceptionを追加する)。私はここでこの例外を捕らえたくないので(私はそれを上方に伝播させて他の場所でそれを適切に処理できるようにしたい)、署名を変更しましょう。

public class Bar implements IFoo {
    @Override
    public void foo() throws InterruptedException {
        throw new InterruptedException();
    }
}

このクラスBarもコンパイルされません!署名が異なるため、Barのメソッドfoo()はIFooのメソッドfoo()をオーバーライドしません。 @Overrideアノテーションを削除することもできますが、IFoo foo;のようにIFooインターフェースに対してプログラムを作成し、後でfoo = new Bar();のようにどの実装を使用するかを決めます。 Barのメソッドfoo()がIFooのメソッドfooをオーバーライドしない場合、foo.foo();を実行しても、Barのfoo()の実装は呼び出されません。

Barのpublic void foo() throws InterruptedExceptionがIFooのpublic void foo()をオーバーライドするようにするには、IFooのメソッドシグネチャにthrows InterruptedExceptionを追加しなければなりません。 foo()メソッドのシグネチャはIFooのメソッドシグネチャとは異なるため、これは私のFooクラスで問題を引き起こします。さらに、Fooのメソッドfoo()にthrows InterruptedExceptionを追加した場合、Fooのメソッドfoo()がInterruptedExceptionをスローすることを宣言しているのにInterruptedExceptionはスローされないことを示す別のエラーが発生します。

お分かりのように(私がこのことを説明するのにまともな仕事をした場合)、InterruptedExceptionのようなチェック済み例外をスローしているという事実は、私のインターフェースIFooをその実装の1つに結びつけなければならない他の実装

これが、チェックされた例外が悪い理由の1つです。キャップで。

1つの解決策は、チェック済みの例外をキャプチャし、それを未チェックの例外でラップして、未チェックの例外をスローすることです。

2
Blueriver
  • Javaは2つのカテゴリの例外(チェックありとチェックなし)を区別します。
  • Javaは、チェック済み例外に対する捕獲または宣言された要件を強制します。
  • 例外のタイプは、例外がチェックされるかどうかを決定します。
  • クラスsubclassesの直接的または間接的なRuntimeExceptionであるすべての例外タイプは未チェックの例外です。
  • クラスExceptionから継承するがRuntimeExceptionから継承しないクラスはすべてchecked exceptionsと見なされます。
  • Errorクラスから継承したクラスはチェックされていないと見なされます。
  • コンパイラは、各メソッド呼び出しと減速をチェックして、メソッドがchecked exceptionをスローするかどうかを判断します。
    • もしそうであれば、コンパイラは例外がキャッチされるか、throws節で宣言されることを保証する。
  • Catch-or-declare要件の宣言部分を満たすために、例外を生成するメソッドは、checked-exceptionを含むthrows句を提供する必要があります。
  • Exceptionクラスは、キャッチまたは宣言するのに十分に重要であると見なされたときにチェックされるように定義されています。
2
tokhi

チェック例外は、外部ライブラリを使用する開発者にとって、例外的な状況ではそのライブラリのコードで問題が発生する可能性があることを思い出させるものだと思います。

未チェック例外と未チェック例外の詳細については、こちらをご覧ください http://learnjava.today/2015/11/checked-vs-unchecked-exceptions/ /

1
Dan Moldovan

これはあなたが決めるのを助けることができる簡単な規則です。これは、Javaでインターフェースがどのように使用されるかに関連しています。

あなたのクラスを取り上げて、そのインターフェイスがそのクラスの機能を記述しているが、その下にあるインプリメンテーションが記述されていないようにそれ用のインターフェイスを設計することを想像してください。おそらくあなたは別の方法でクラスを実装するかもしれないふりをします。

インターフェースのメソッドを調べて、それらがスローする可能性のある例外を検討してください。

基礎となる実装に関係なく、メソッドによって例外がスローされる可能性がある場合(つまり、機能のみが記述されている場合)は、おそらくインタフェースでチェック済みの例外になるはずです。

基になる実装によって例外が発生した場合、それはインタフェース内にあってはいけません。したがって、それはあなたのクラスの未チェック例外であるか(未チェック例外はインタフェースシグネチャに現れる必要はないので)、あるいはそれをラップしてインタフェースメソッドの一部であるチェック例外として再投げなければなりません。

ラップして再スローするかどうかを判断するには、インターフェイスのユーザーが例外条件をただちに処理する必要があるかどうかを検討する必要があります。そうでない場合は例外が発生します。スタックを伝播します。ラップした例外は、定義している新しいインタフェースの機能として表現した場合に意味がありますか。それとも、他のメソッドでも発生する可能性がある一連のエラー条件のキャリアにすぎませんか。前者の場合、それはまだチェックされた例外かもしれません、そうでなければチェックされないはずです。

通常は、例外を「バブルアップ」させること(キャッチアンドリスロー)を計画しないでください。例外は呼び出し側によって処理される(その場合はチェックされる)か、または高レベルのハンドラーに到達するはずです(この場合、チェックされないのが最も簡単です)。

1
rghome

つまり、上記の1つまたは複数のモジュールが実行時に処理することになっている例外は、チェック済み例外と呼ばれます。その他はRuntimeExceptionまたはErrorのいずれかである未チェックの例外です。

このビデオでは、Javaでのチェック済みおよび未チェックの例外について説明します。
https://www.youtube.com/watch?v=ue2pOqLaArw

1
Ozgen

例外が発生するのはなぜですか。エラーをより早く処理していませんか?なぜバブルアップ?

例えば、あなたが client-serverアプリケーションを持っているとしましょう そして、クライアントが見つけることができなかった何らかのリソースまたは他の何かのエラーのためのリクエストをしました。要求したものを取得できなかった理由をクライアントに通知するのはサーバーの義務です。そのため、サーバー側では、飲み込みや処理の代わりに throw キーワードを使用して例外をスローするようにコードを記述します。 it.ifサーバーがそれを処理したり飲み込んだりすると、どんなエラーが発生したのかをクライアントに知らせることはできなくなります。

注:どのようなエラータイプが発生したのかを明確に説明するために、独自のExceptionオブジェクトを作成してクライアントにスローできます。

1
JAVA

コード内でチェック例外をスローし、そのキャッチが数レベル上である場合は、自分とキャッチの間の各メソッドのシグネチャでその例外を宣言する必要があります。そのため、throwのパスにあるすべての関数がその例外の詳細について知っている必要があるため、カプセル化は壊れています。

1
Zoran Pandovski

それらはすべてチェック例外です。未チェックの例外はRuntimeExceptionのサブクラスです。決定はそれらをどのように処理するかではありません、それはあなたのコードがそれらを投げるべきです。例外を処理していないとコンパイラに通知されたくない場合は、未チェックの(RuntimeExceptionのサブクラス)例外を使用します。これらは、メモリ不足エラーなどのように、回復できない状況に備えて保存する必要があります。

0
mamboking