Java Core Technology Interview Essentials(講義2)|例外とエラーの違いは何ですか?

決して失敗しないプログラムは世界にありますか?たぶん、これはプログラマーの夢にしか現れないでしょう。プログラミング言語とソフトウェアの誕生により、異常な状況が影のように私たちを巻き込みます。予期しないものを正しく処理することによってのみ、プログラムの信頼性を保証できます。

Java言語は、設計の初期に比較的完全な例外処理メカニズムを提供します。これが、信頼性の高いプログラムを作成および維持するためのしきい値を大幅に削減するため、Javaが非常に普及した理由の1つです。今日、例外処理メカニズムは、最新のプログラミング言語の標準構成になっています。

今日お聞きしたいのは、例外とエラーを比較してください。また、実行時例外と一般例外の違いは何ですか?


典型的な答え

ExceptionとErrorはどちらもThrowableクラスを継承します。Javaでは、Throwableタイプのインスタンスのみをスロー(throw)またはキャッチ(catch)できます。これは、例外処理メカニズムの基本的なコンポーネントタイプです。

例外とエラーは、Javaプラットフォームの設計者によるさまざまな例外の分類を反映しています。例外は、プログラムの通常の操作中に予想される予期しない状況であり、それに応じてキャッチおよび処理する必要があります。

エラーとは、通常の状況では発生する可能性が低い状況を指します。ほとんどのエラーにより、プログラム(JVM自体など)が異常で回復不能な状態になります。異常な状況であるため、不便であり、キャプチャする必要はありません。OutOfMemoryErrorなどの一般的なものはすべてErrorのサブクラスです。

例外は、チェックされた例外とチェックされていない例外に分けられます。チェック可能な例外は、ソースコードで明示的にキャプチャする必要があります。これは、コンパイル時のチェックの一部です。以前に紹介したチェックできないエラーは、例外ではなくスロー可能です。

未チェックの例外は、NullPointerException、ArrayIndexOutOfBoundsExceptionなどと同様のいわゆるランタイム例外であり、通常はコーディングによって回避できる論理エラーです。特定の例外は、キャプチャが必要かどうかを判断する必要があり、コンパイル時に必須ではありません。

テストサイト分析

例外とエラーの違いを分析することは、概念的な観点からJava処理メカニズムを調べることです。一般的に言って、インタビュアーが明確に詳しく説明している限り、それはまだ理解のレベルにあります。

私たちの日常のプログラミングでは、例外に対処する方法は知識のテストです。私たちは2つの側面を習得する必要があると思います。

まず、Throwable、Exception、およびErrorの設計と分類を理解します。たとえば、最も広く使用されているサブカテゴリや、例外をカスタマイズする方法をマスターします。

多くのインタビュアーはさらにいくつかの詳細を尋ねます。たとえば、どのエラー、例外、またはRuntimeExceptionを知っていますか?私は簡単なクラス図を描き、典型的な例をリストしました。参考として、少なくとも基本的な知識を身に付けることができます。

これらのサブタイプのいくつかは、たとえば、NoClassDefFoundErrorとClassNotFoundExceptionの違いを理解することに焦点を当てるのが最善です。これも、古典的なエントリトピックです。

次に、Java言語でThrowableを操作するための要素とプラクティスを理解します。try-catch-finallyブロック、throw、throwsキーワードなど、最も基本的な構文を習得する必要があります。同時に、典型的なシナリオに対処する方法も知っておく必要があります。

例外処理コードはもっと面倒です。たとえば、たくさんのクッキーカッターキャプチャコードを書くか、最終的にいくつかのリソース回復作業を行う必要があります。Java言語の開発に伴い、try-with-resourcesやmultiple catchなど、より便利な機能がいくつか導入されました。詳細については、次のコードスニペットを参照してください。コンパイル中に、対応する処理ロジックが自動的に生成されます。たとえば、AutoCloseableまたはCloseableを拡張するオブジェクトは、規則に従って自動的に閉じられます。

try (BufferedReader br = new BufferedReader(…);
     BufferedWriter writer = new BufferedWriter(…)) {// Try-with-resources
// do something
catch ( IOException | XEception e) {// Multiple catch
   // Handle it
} 

知識の拡大 

これまでの話のほとんどは概念的なものです。次に、いくつかの実用的な選択について説明します。いくつかのコードのユースケースと併せて分析します。

最初のものから始めましょう。次のコードは、例外処理の何が問題になっているのかを反映していますか?

try {
  // 业务代码
  // …
  Thread.sleep(1000L);
} catch (Exception e) {
  // Ignore it
}

 このコードは非常に短いですが、例外処理の2つの基本原則に違反しています。

まず、Exceptionのような一般的な例外をキャッチするのではなく、特定の例外(この場合はThread.sleep()によってスローされるInterruptedException)をキャッチするようにしてください。

これは、日々の開発や協力において、コードを書くよりもコードを読む機会が多いためです。ソフトウェアエンジニアリングはコラボレーションの芸術であるため、コードにできるだけ多くの情報を直感的に反映させることが義務付けられており、一般的です。 。例外などは私たちの目的を隠します。さらに、プログラムがキャッチしたくない例外をキャッチしないようにする必要もあります。たとえば、RuntimeExceptionをキャッチするのではなく、拡散することをお勧めします。

さらに、考え直さない限り、ThrowableやErrorをキャッチしないでください。OutOfMemoryErrorを正しく処理できることを確認するのは困難です。

第二に、異常を飲み込まないでください。これは、診断が非常に難しい奇妙な状況を引き起こす可能性があるため、例外処理で特に注意を払う必要があります。

例外を飲み込むことは、多くの場合、このコードが発生しない可能性があるという仮定に基づいています。または、例外を無視することは重要ではありませんが、製品コードではそのような仮定を行わないでください。

例外をスローしない場合、またはログ(Logger)に出力しない場合、プログラムは後続のコードで制御不能な方法で終了する可能性があります。例外がスローされた場所と、例外の原因を簡単に特定することはできません。

2番目のコードを見てみましょう

try {
   // 业务代码
   // …
} catch (IOException e) {
    e.printStackTrace();
}

 このコードは実験的なコードの一部として使用されており、問題はありませんが、製品コードでは通常、処理することは許可されていません。これがなぜなのか考えますか?

「このスロー可能オブジェクトとそのバックトレースを標準エラーストリームに出力する」で始まるprintStackTrace()のドキュメントを見てみましょう。問題はここにあります。少し複雑な本番システムでは、出力の行き先を判断するのが難しいため、STERRは適切な出力オプションではありません。

特に分散システムの場合、例外が発生してもスタックトレースが見つからない場合、これは純粋に診断の障害になります。したがって、製品ログを使用して、ログシステムに詳細に出力することをお勧めします。

次のコードスニペットを見て、スローを早く、キャッチを遅くする原則を体験してみましょう。

public void readPreferences(String fileName){
   //...perform operations... 
  InputStream in = new FileInputStream(fileName);
   //...read the preferences file...
}

 fileNameがnullの場合、プログラムはNullPointerExceptionをスローしますが、問題が最初に公開されないため、スタック情報が非常にわかりにくく、比較的複雑な配置が必要になることがよくあります。このNPEは単なる例であり、実際の製品コードでは、構成の取得に失敗するなど、さまざまな状況が発生する可能性があります。問題が発見された場合は、できるだけ早く破棄することができ、問題をより明確に反映できます。

質問を「早く投げる」ように変更でき、対応する例外情報は非常に直感的です。

public void readPreferences(String filename) {
  Objects. requireNonNull(filename);
  //...perform other operations... 
  InputStream in = new FileInputStream(filename);
   //...read the preferences file...
}

「遅れる」というのは、実はよく気になる問題ですが、例外を見つけたらどうすればいいですか?最悪の対処法は、先ほど申し上げた「嚥下異常」で、実際に問題を隠蔽することです。対処方法が本当にわからない場合は、元の例外の原因情報を保持して直接スローするか、新しい例外を作成してスローするかを選択できます。より高いレベルでは、明確な(ビジネス)ロジックにより、適切な処理方法がより明確になることがよくあります。

場合によっては、必要に応じて例外をカスタマイズします。現時点では、十分な情報が提供されていることを確認することに加えて、考慮すべき点が2つあります。

  • このタイプの設計の本来の目的は異常な状況から回復することであるため、チェック例外として定義する必要があるかどうか。例外設計者として、分類するのに十分な情報を持っていることがよくあります。
  • 診断情報が十分であることを確認する一方で、潜在的なセキュリティ問題につながる可能性があるため、機密情報を含めないようにすることも検討してください。Java標準ライブラリを見ると、java.net.ConnectExceptionのようなものに気付くかもしれません。エラーメッセージは「接続が拒否されました(接続が拒否されました)」に似ており、特定のマシン名、IP、ポートなどが含まれていません。重要な考慮事項は情報のセキュリティです。同様の状況がログに存在します。たとえば、ユーザーデータは通常ログに出力されません。

業界では、Java言語のチェック例外が設計エラーである可能性があるという一種の議論があります(ある程度のコンセンサスと見なすこともできます)。反対派はいくつかの点を挙げています。

  • Checked Exceptionの前提は、例外をキャッチしてからプログラムを再開することです。ただし、ほとんどの場合、回復することは不可能です。Checked Exceptionの使用は、元の設計目的から大きく逸脱しています。
  • CheckedExceptionは機能プログラミングと互換性がありません。Lambda/ Streamコードを記述している場合は、深く理解していると思います。

Spring、Hibernateなど、多くのオープンソースプロジェクトがこの手法を採用しており、Scalaなどの新しいプログラミング言語の設計にも反映されています。興味のある方は、以下を参照してください。

http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/

パフォーマンスの観点からJavaの例外処理メカニズムを見てみましょう。比較的コストがかかる可能性のあるものが2つあります。

  • try-catchコードセグメントは、追加のパフォーマンスオーバーヘッドを引き起こすか、言い換えると、コードを最適化するためにJVMに影響を与えることが多いため、必要なコードセグメントのみをキャプチャし、コード全体をカバーしないようにすることをお勧めします。大きな試み;同時に、例外を使用してコードフローを制御することはお勧めできません。通常の条件ステートメント(if / else、switch)よりもはるかに効率が低くなります。
  • Javaが例外をインスタンス化するたびに、現在のスタックのスナップショットが作成されますが、これは比較的重い操作です。非常に頻繁に発生する場合、このオーバーヘッドは無視できません。

したがって、極端なパフォーマンスを追求する一部の基盤となるライブラリの場合、1つの方法は、スタックスナップショットを取得しない例外を作成しようとすることです。これ自体は物議を醸しています。なぜなら、例外を作成するときに、スタックが将来必要かどうかがわかるという前提があるからです。問題は、それは実際に可能ですか?小規模では可能かもしれませんが、大規模なプロジェクトでは、そうすることは賢明な選択ではないかもしれません。スタックが必要であるが、この情報が収集されない場合、複雑な状況、特にマイクロサービスのような分散システムでは、これにより診断が大幅に困難になります。

サービスの速度が低下し、スループットが低下した場合、最も頻繁に発生する例外をチェックすることも考え方です。バックグラウンドの遅さの診断の問題については、次のJava Performance BasicModuleで体系的に説明します。

今日は、例外処理の一般的な概念上の問題から、Java例外処理のメカニズムを簡単に要約しました。そして、コードと組み合わせて、一般的に認識されているいくつかのベストプラクティスと、異常な使用に関する業界の最新のコンセンサスのいくつかを分析しました。最後に、異常なパフォーマンスのオーバーヘッドを分析しました。お役に立てば幸いです。


他の古典的な答え

次の答えは、迷子になって理由を知っているネチズンからのものです。

料理を比較していると、「NoClassDefFoundErrorとClassNotFoundExceptionの違いは何ですか?これも古典的なエントリートピックです」と聞きました。この段落では、2つの違いについてお話したいと思いました。この違いは乾いたものだと思います。もの!記事はより決定的な言葉を持っており、具体的ではありません

ネチズンマオマオシオンは答えた:

NoClassDefFoundErrorはエラー(エラー)であり、ClassNOtFoundExceptionは例外です。Javaでのエラーと例外の処理は異なります。例外からプログラムを回復することはできますが、エラーからプログラムを回復しようとしないでください。

ClassNotFoundExceptionの理由:

Javaは、Class.forNameメソッドを使用してクラスを動的にロードすることをサポートしています。クラスのクラス名がパラメーターとしてこのメ​​ソッドに渡されると、クラスがJVMメモリにロードされます。このクラスが見つからない場合クラスパスで、実行時にClassNotFoundException例外をスローします。

ClassNotFoundExceptionの理由:

Javaは、Class.forNameメソッドを使用してクラスを動的にロードすることをサポートしています。クラスのクラス名がパラメーターとしてこのメ​​ソッドに渡されると、クラスがJVMメモリにロードされます。このクラスが見つからない場合クラスパスで、実行時にClassNotFoundException例外をスローします。

ClassNotFoundExceptionの主な理由は次のとおりです。

Javaは、実行時にクラスを動的にロードするためのリフレクションの使用をサポートしています。たとえば、Class.forNameメソッドを使用してクラスを動的にロードする場合、クラス名をパラメーターとして上記のメソッドに渡して、指定したクラスをJVMメモリにロードできます。 。クラスがクラス内にある場合パスが見つからない場合、この時点で実行時にClassNotFoundExceptionがスローされます。

この問題を解決するには、必要なクラスとそれに依存するパッケージがクラスパスに存在することを確認する必要があります。一般的な問題は、クラス名が正しく記述されていないことです。

ClassNotFoundExceptionのもう1つの原因は、特定のクラスローダーによってクラスがメモリにロードされたときに、別のクラスローダーが同じパッケージからこのクラスを動的にロードしようとすることです。動的クラスロードプロセスを制御することにより、上記の状況を回避できます。

NoClassDefFoundErrorの理由は次のとおりです。

JVMまたはClassLoaderインスタンスがクラスをロードしようとすると(通常のメソッドで呼び出すことも、newを使用して作成することもできます)、クラスの定義が見つかりません。検索対象のクラスは、コンパイル時には存在しますが、実行時には見つかりません。このとき、NoClassDefFoundErrorが発生します。

この問題の原因は、パッケージングプロセス中に一部のクラスが欠落しているか、jarパッケージが破損または改ざんされている可能性があります。この問題の解決策は、開発中にクラスパスに存在するが、実行時にクラスパスに存在しないクラスを見つけることです。

以下は、ネチズンのadrian-jserからの回答です。

山をドライブして車が故障した場合、ツールボックスを取り出して修理してから、道路を続行します(例外がキャッチされ、例外から回復して、プログラムを実行し続けます)。車は故障します。 、そしてあなたはそれを修正する方法がわからないので、修理業者に電話して修理業者に伝え、問題が何であるかを教えて、そしてディーラーに来てそれを修理するように頼んでください。(現在の論理的な背景では、処理のために上位のビジネス層に例外をスローする処理ロジックの種類がわかりません)。電話をかけるときは、車が動かないというだけでなく、できるだけ具体的にしてください。車の修理店があなたの問題を見つけるのは難しいです。(特定の例外を補充するために、例外と同様の一般的な例外をキャッチすることはできません)。もう一つの状況は、あなたが山を運転して山が崩壊した場合でも、あなたはそれを修理することができるということです。(エラー:動作環境が異常な状態になり、回復が困難になります)

次の答えは、ネチズンのOuyangTianからのものです。

1.エラー:システムエラー、仮想マシンエラー、処理できず、処理する必要がありません。

2.例外、キャッチして処理できる例外。つまり、例外をキャッチして処理するか、例外をスローし続けます。

3.頻繁に発生するエラーであるRuntimeExceptionは、キャッチして処理できます。キャッチまたはスローされない場合があります。この例外のようなArrayIndexOutOfBoundsExceptionはキャッチできません、なぜですか?プログラムでは、多くの配列が使用されており、1つのキャプチャを1回使用すると、非常に面倒です。

4.例外を継承するとき、メソッドを書き換えるとき、例外がスローされないか、まったく同じ例外がスローされます。

5.試行の後に多くのキャッチが続く場合、最初に小さな例外をキャッチしてから、大きな例外をキャッチする必要があります。

6.例外が発生した場合、コンソールは多くの行の情報を出力します。これは、プログラム内の多層メソッド呼び出しが原因です。重要なのは、タイプと行番号を確認することです。

7.アップロードとダウンロードで例外をスローすることはできません。アップロードとダウンロードを閉じる必要があります。

8.例外はエラーではありません。例外制御コードフローは、単純で読みやすいコードを助長するものではありません。

9. return、break、continueなどを組み合わせたcatchfinally実行フローを試してください。コード実行の順序に注意してください。それは不可能ではありませんが、人が強力であればあるほど、コードを理解しやすくなります。

以下は、ネチズンからの回答です。

エラーとは、プログラムのクラッシュを引き起こす可能性のある予測できないエラーを指します。例外とは、プログラムの動作における予測可能な異常を指し、例外は、チェック例外と一般例外に分けられます。チェック例外は、プログラムに表示およびキャプチャして処理する必要があります。 、一般的な例外は、範囲外の配列、ヌルポインタなど、プログラムコーディングによって処理できます。例外処理の2つの基本原則:例外を直接キャプチャするなど、一般的な例外情報をキャプチャしないでください。コードの難易度が高くなります。読み取り;例外を飲み込まない;例外情報の出力は比較的重い操作であり、プログラムの速度が低下します; try catchには、例外をチェックする必要のあるコードを含めることが望ましく、長すぎるコードを含めないでください。 JVMの最適化効率;これはこのレッスンを学ぶことです要約の一部

 

 

おすすめ

転載: blog.csdn.net/qq_39331713/article/details/114025666