web-dev-qa-db-ja.com

ファイルなどのリソースを閉じるときに例外を無視しますか?

効果的なJava 2e from Joshua Bloch、

例外を無視することが適切であるような状況の例は、FileInputStreamを閉じるときです。ファイルの状態を変更していないため、リカバリアクションを実行する必要はありません。また、ファイルから必要な情報をすでに読んでいるため、進行中の操作を中止する理由はありません。この場合でも、例外をログに記録して、これらの例外が頻繁に発生するかどうかを調査できるようにすることをお勧めします。

提案されているように、常にその例外を無視することは賢明ですか?コードは次のようになります。

FileInputStream stream = null;
try {
    stream = new FileInputStream("foo.txt");
    // do stuff
} finally {
    if (null != stream) {
        try {
            stream.close();
        } catch (IOException e) {
            LOG.error("Error closing file. Ignoring this exception.", e);
        }
    }

これよりもはるかに冗長です

try (FileInputStream stream = new FileInputStream("foo.txt")) {
    // do stuff
}

しかし、ファイルを閉じることによる例外を無視する価値がある余分な冗長性はありますか?何もする必要がないので、例外を無視した方がいいですか?データベース接続など、ファイル以外のリソースでも同じアプローチを使用する必要がありますか?

5
Max

効果的な第3版Javaには、「try-with-resourcesをtry-finallyより優先する」というタイトルの新しいセクションが含まれています。その中で、Blochは次のように書いています。クローズする必要があるリソースを操作するときは、最終的にトライすることを優先します」

  • Finallyブロックの例外は、try949の例外を非表示にします。これは、user949300が回答で説明しているとおりです。

  • 複数のリソースで使用した場合、try-finallyは読みづらくなります。

  • try-finallyは誤って使用されることがよくあります。 「実際には、Javaライブラリでのcloseメソッドの使用の3分の2は2007年には間違っていました。」

したがって、質問に答えるために、Blochは、try-with-resourcesはその価値があると考えているようです。

1
Max

Blochが説明しているように、その理由は興味深い例外がtryブロック内からスローされた元の例外であるためです。 finallyブロック内でスローされた例外非表示になります元の例外。

Ur-Exceptionがファイルがネットワーク上にあり、ネットワークがダウンしていることが原因であると想像してください。それはあなたが投げたいより有益な例外です。しかし、その問題はclose()にも影響し、2番目の例外が「邪魔になる」ことは望ましくありません。

この説明は彼の本のV1にありました...

追加次のコードは要点を示しています。

try {
  throw new Exception("in try, here is the ur-Exception");
}
finally {
  throw new Exception("in finally");
}

2番目の「最終的に」例外は、元の例外を非表示にするものです。

3
user949300

議論のために、私はこの操作がどのように失敗するのか、そしてそれが失敗すると何がうまくいかないのか想像できません。

したがって、私は本番環境にログインする以外は何もしません。また、開発者のマシンで実行するときに、アプリがブレークポイントに到達することを確認してください。これが失敗する場合は、それについて、またなぜ発生したのかを知りたいので、その処理方法をより正確に理解できます。

2
gnasher729

一般に、例外をスローせずにプログラムが実行されることを期待する必要があります。

したがって、例外がスローされた場合、それを無視するのは珍しいことです。

ただし、プログラムをクラッシュさせたくないのはごく一般的なことです。したがって、人々はアプリケーションを完全にクラッシュさせることを考えていなかったものを止めるために、トップレベルのtry catch catchブロックを何もしないようにします。

この場合、実際には、考えていなかった例外をログに記録したいので、コードを更新して適切に処理できます。

FileInputStreamの例では、はい、これが例外をスローする理由(おそらくファイルが削除されている可能性があります)、または実行された場合にどうするかは明確ではありません。

おそらく、クローズが正常に完了しなかった場合、メモリリークが発生する可能性があります。だから、それがたくさん起こっているなら、あなたはそれについて知りたいでしょう。

1
Ewan

その時そして今、時々。はい。しかし、いつも?神によって-決して!

最初の注意:正確な例からルールを過度に一般化することについては注意が必要です。

比較する

_void close(Closable target) {
    try {
        target.close();
    } catch (IOException e) {
        ???
    }
}
_

この例外を重要でないと見なすのに十分な情報がありません。リソースが正常に閉じるのを妨げる 多数例外的な状況 があります。

_void close(FileInputStream target) {
    try {
        target.close();
    } catch (IOException e) {
        ???
    }
}
_

ここでは、データを読み取っていて(破損の心配はありません)、呼び出し元が予期したデータを既に取得している(呼び出し元がclose()を呼び出している)という追加のコンテキストがあります。言い換えると、例外的な状況にありますが、状況は、呼び出し側が明示的な postcondition を保証できないことを示していません。

ビジネス価値が発生する場合と、実際のデータ損失の兆候がある場合とでファイルハンドルがリークすることを混同しないでください。

私は個人的にverbosity;にこだわりすぎないでしょう。別のClosableを使用して作業を行うことができます

_try( ClosePolicy closer = new LoggingClosePolicy(new FileInputStream(...))) {
    FileInputStream stream = closer.get();
    // ...
}
_

これは、意図を明らかにするスケールでは少し優れていると思います。クローズ時の例外のデフォルトの戦略は満足できるものではないため、置き換えます。

がれきが取り除かれたら、元の質問に取り掛かることができると思います。これは確認済みの例外なので、可能性を受動的に無視することはできません。

2つの可能性があります。ローカルメソッドが例外に直面しても事後条件を確立できない場合は、何らかの形でそれを伝播する必要があると思います。

しかし、ローカルメソッドがポスト条件のobservable部分を確立できることがより一般的であると思います。この場合、log-and-move -onは、例外的な状況で即時の修復が必要ない場合にビジネス価値を獲得するための合理的な方法のようです。

1
VoiceOfUnreason

この問題はJava言語、または例外のある問題の言語に限定されないことに注意してください。Cでプログラミングするときにまったく同じ問題に直面する可能性があります。fcloseからの戻り値を確認しますか? ()?またはclose()から?

私は両方のアプローチを見ました:チェックとチェックではありません。個人的には、ファイナライザがエラーコードを返さないようにコーディングし、すべてのエラーを黙って無視します。場合によっては、バグの検出に関心があるときに、重要な前提条件に違反していることがわかった場合に、abort()を呼び出すことにより、大きな失敗をします。残されたコアダンプはすべてを説明します。言うまでもなく、最終的なプログラムでは、abort()関数が呼び出されることはありません。たとえば、abort()を使用しないでください。ネットワーク接続または一部の外部リソースが失敗した。それは前提条件のためだけです!

ファイルの場合、おそらくそれを閉じることに失敗しないでしょう。ただし、一部のネットワークストリームの場合、エラー状態が発生する可能性があります。

また、例外をログに記録してから、黙って無視することもできます。ログにそのような例外がたくさんある場合は、最大のログサイズ違反者のログをオフにすることができます。

1
juhist

私にとっては、例外に関する簡単なルールが1つあります。

メソッドがその契約を満たさない場合、例外がスローされます。

現在、メソッドのコントラクトには明示的な側面と暗黙的な側面があります。明示的なコントラクトは、通常(うまくいけば)名前と相関関係があります。暗黙的なコントラクトパーツは、ファイルシステムをメソッド呼び出し前と同じ状態のままにし、リソースリークを引き起こさないなどの可能性があります。これは設計の決定であり、特定のメソッドが持つ暗黙の義務です。

あなたの方法の契約は何ですか?

  • 例えば。構成ファイルからデータを読み取る。

ストリームを閉じるときの失敗の影響は何ですか?

  • ファイルは正常に読み取られたため、明示的な契約が履行されました。
  • FileInputStreamを閉じないと、リソースリークが発生します。アプリケーションの実行時に1回読み込まれる単一の構成ファイルの場合、これはほとんど問題になりません。したがって、通常は暗黙的な規約に違反しません。
  • FileInputStreamを閉じないと(環境によっては、OS、ファイルシステムなど)、ファイルがロックされたままになり、他のプロセスによる後の変更が禁止される場合があります。通常、構成ファイルは頻繁に変更されないため(変更された場合はアプリケーションを再起動する必要があります)、これも読み取りメソッドの許容可能な動作です。

つまり、この1つの構成ファイルを読み取る場合、例外をWARNレベルでログに記録し、例外なしで戻ります。

ただし、メソッドがファイルマネージャーアプリケーションのfileCopy()関数である場合、ファイルをロックするリスクがあるソースファイルを閉じないと、暗黙のコントラクトに確実に違反するため、例外が必要になります。

1
Ralf Kleberhoff