19

Java I/O クラスjava.io.Readerjava.io.Writerjava.io.InputStreamjava.io.OutpuStreamおよびそれらのさまざまなサブクラスにはすべて、close()をスローできるメソッドがありIOExceptionます。

そのような例外を処理する適切な方法についてコンセンサスはありますか?

黙ってそれらを無視するようにという推奨事項をよく見てきましたが、それは間違っているように感じます。少なくとも、書き込み用に開かれたリソースの場合、ファイルを閉じるときに問題が発生すると、フラッシュされていないデータを書き込んだり送信したりできない可能性があります。

一方、リソースを読んでいると、なぜclose()スローされるのか、どうすればよいのかがまったくわかりません。

それで、標準的な推奨事項はありますか?

関連する質問は、close が IOException をスローすることはありますか? 、しかしそれは例外を処理する方法ではなく、どの実装が実際にスローするかについてです

4

9 に答える 9

7

ログに記録します。

実際には何もできませんが(たとえば、エラーから回復するコードを作成するなど)、一般的に誰かに知らせる価値があります。

編集:
さらに調査して他のコメントを読んだ後、それを処理したい場合は、実装の詳細を知る必要があると思います。逆に、実装を処理する必要があるかどうかを判断するには、おそらく実装の詳細を知る必要があります。

ただし、現実的には、例外をスローせずに読み取りまたは書き込みが正しく機能するストリームの例は考えられませんが、クロージングは​​例外をスローします。

于 2011-01-13T11:16:18.723 に答える
4

「無視するか、単にログに記録することは通常、悪い考えです。」それは質問に答えていません。close() からの IOException についてはどうすればよいですか? それを投げ直すだけでは、問題がさらに深刻になり、処理がさらに困難になります。

仮説

質問に直接答えるには: この IO アクションが失敗した場合、状況によっては、

  • 変更のロールバック (部分的に書き込まれたファイルをディスクに残さず、削除してください)
  • 再試行 (おそらくロールバック後)
  • 無視する (そして続行する)
  • 現在のタスクを中断する (キャンセル)

多くの場合、ユーザーに表示されるダイアログ ボックスで最後の 3 つを確認できます。実際、ユーザーに選択を委任することは可能です。

システムを矛盾した状態にしないことがポイントだと思います。クローズ例外を飲み込むだけで、ファイルが機能しなくなり、後で厄介なエラーが発生する可能性があります。

練習

チェック例外を扱うのは少し面倒です。オプションが発生します:

  • それらを に変換し、それらをRuntimeException投げて、十分に高いレベルで処理されるようにします。

  • チェック例外を使用し続け、構文上の問題に苦しむ

  • IO モナドのような、より構成可能なエラー処理構造を使用します (Java では実際には使用できません。少なくとも、恥ずかしいほど多くの中かっこがなければ使用できません ( http://apocalisp.wordpress.com/2008/05/16/thrower-functor/を参照) 。全力で)

IO モナドの詳細

IO モナド コンテキストで結果を返す場合、実際に IO アクションを実行するのではなく、そのアクションの説明を返します。なんで?これらのアクションには、適切に構成する API があります (throws/try/catch の大虐殺に対して)。

例外スタイルの処理をチェックするかどうかを決定できます (withOptionまたはValidation˙ in the return type), or dynamic style handling, with bracketing only on the finalunsafePerformIO` 呼び出し (構成された IO アクションを実際に実行します))。

アイデアを得るには、メソッドをホストする私の Scala 要点http://gist.github.com/2709535を参照してください。executeAndCloseリソース処理の結果 (利用可能な場合) と閉じられていないリソース (クローズに失敗した場合) の両方を返します。このようなクローズできないリソースをどのように処理するかは、ユーザーが決定します。

1 つまたは複数の実際の例外が必要な場合は、代わりにValidation/を使用して強化することもできます。ValidationNELOption

于 2012-05-16T11:11:00.653 に答える
1

高レベルの計算を実行するために低レベルまたはクラスに委譲する高レベル コードを呼び出すInputStream.close()、または高レベル コードであるコード。OutputStream.close()InputStreamOutputStream

例外をスローするかどうか

何をするかについては、3 つの選択肢しかありません。

  • 例外をキャッチして飲み込み、破棄します。
  • 上に伝播させましょう。
  • 例外をキャッチし、別の種類の例外をスローします。この場合、通常は連鎖例外を使用します。

最後の 2 つのオプションは、コードが例外をスローするという点で似ています。したがって、この質問は実際には、コードがいつ例外をスローする必要があるかという質問の特殊なケースです。このコンテキストでのその質問に対する正しい答えは、次のとおりです。代替手段が、コードの事後条件を満たさないか、コードの不変条件を維持することである場合に限ります。事後条件は、メソッドの目的を指定します。不変条件は、クラスの特性を指定します。

したがって、close()例外をスローすることで、メソッドが本来すべきことを実行できなくなるかどうかを自問する必要があります。

  • メソッドがその仕事をするのを妨げない場合、正しいことは例外を飲み込むことです。多くの人があなたに言うことにもかかわらず。
  • close()スローによってメソッドがジョブを実行できず、メソッドが をスローする可能性がある場合は、IOException何もできず、例外を上に伝播させるだけです。
  • close()メソッドがジョブを実行できないが、メソッドが をスローしない可能性がある場合は、IOExceptionをキャッチしIOExceptionて別のクラスの例外として再スローし、スローされた例外IOExceptionの原因として を記録する必要があります。

InputStream.close()例外のスローによって計算の成功が妨げられる状況を私は知りません。への呼び出しは、関心のあるデータを読み終わった後close()に自然に発生します。

ただし、出力ストリームは、内部 (Java コード内) または下位レベル (オペレーティング システム内) のいずれかで、出力先に書き込まれるデータをバッファリングできます。したがって、(例外をスローせずOutputStream.close()に)が正常に戻るまで、出力ストリームへの書き込み操作が実際に実際の出力になったことを確認することはできません。したがって、 によってスローされた例外は、書き込みの失敗と同じように扱う必要があります。OutputStream.close()

メソッドは、その不変条件を維持する責任があります。close()例外がスローされた場合、クリーンアップまたはロールバック操作が必要になることがあります。例外を上方に伝搬させたい場合でも、そのコードを のcatchorfinally節に入れる必要があります。IOExceptioncatch 句を使用していて、それを伝播したい場合は、再スローする必要があります。

例外をログに記録するかどうか

例外をログに記録するようアドバイスする人もいます。これはほとんどの場合、悪いアドバイスです。ログ メッセージは、プログラムのユーザー インターフェイスの一部です。役に立たない言い回しは、ログ ファイルを読んでいるユーザー (「ユーザー」にはシステム管理者を含みます) の注意をそらし、混乱させる可能性があるため、基本的に、何かをログに記録するかどうかを常に自問する必要があります。ログに記録されたすべてのメッセージは、何らかの有用な目的である必要があります。それぞれが、ユーザーの意思決定に役立つ情報を提供する必要があります。

失敗したことを具体的に報告することclose()はほとんど役に立ちません。ユーザーの意思決定にどのように役立つでしょうか? 例外によってメソッドの動作が妨げられなかった場合、問題はなく、ユーザーによるアクションは必要ありません。close()プログラムがストリームに失敗し、それ問題である場合、ユーザーは何ができるでしょうか?

通常、低レベルのコードはログ記録をまったく担当しません。代わりに抽象的な操作を実行し、例外をスローしてプログラムの上位レベルの部分に失敗を報告します。通常、ストリームを閉じるコードはかなり低レベルであるため、それを検出するコードclose()は低レベルすぎてログを記録できません。

失敗したという特定の事実がclose()役立つことはめったにありません。役に立つのは、メソッドが実行するはずの抽象操作が失敗したことを知ることです。これは、メソッドに「クローズに失敗しました」と正確に報告させるのではなく、より高いレベルのコードに予想されるすべての例外をキャッチさせ、操作が失敗したことを報告させることで実行できます。

于 2018-09-08T13:36:35.037 に答える
0

それはあなたが何を閉じているかに依存します。たとえば、aを閉じてStringWriterも何も起こりません。javadocsはそれを明確にしています。この場合、IOExceptionは生成されないことがわかっているため、無視できます。技術的には、を呼び出す必要はありませんclose

/**
 * Closing a <tt>StringWriter</tt> has no effect. The methods in this
 * class can be called after the stream has been closed without generating
 * an <tt>IOException</tt>.
 */
public void close() throws IOException {
}

他のストリームの場合は、ログに記録して、必要に応じて例外を処理します。

于 2011-01-13T11:37:11.240 に答える
0

まず、try catchブロックの「finally」セクション内にclose()を配置することを忘れないでください。次に、closeメソッドを別のtry catch例外で囲み、例外をログに記録できます。

于 2011-01-13T11:29:04.627 に答える
0

通常、リソースの処理は次のようになります。

final Resource resource = context.acquire();
try {
    use(resource);
} finally {
    resource.release();
}

(めったにありませんが)release例外をスローする場合、によってスローされた例外useは破棄されます。キャッチャーの勝利に最も近い例外がスローされることに問題はありません。この場合、JDK7(?) の ARM ブロックは、use例外をアタッチして例外を再スローするなど、おかしなことをすると思いreleaseます。

Execute Around イディオムを使用すると、これらの決定と潜在的に厄介なコードを 1 か所 (または少数) に配置できます。

于 2011-01-13T13:09:07.630 に答える
0

無視するか、単にログに記録することは、通常は悪い考えです。そこから回復できないのは事実ですが、ソフトウェアが統一された方法で動作することを確認するために、I/O 例外は常に統一された方法で処理する必要があります。

少なくとも以下のように処理することをお勧めします。

//store the first exception caught
IOException ioe = null;
Closeable resource = null;
try{
  resource = initializeResource();
  //perform I/O on resource here
}catch(IOException e){
  ioe = e;
}finally{
  if (resource != null){
    try{
      resource.close();
    }catch(IOException e){
      if(null == ioe){
        //this is the first exception
        ioe = e;
      }else{
        //There was already another exception, just log this one.
        log("There was another IOException during close. Details: ", e);
      }
    }
  }
}

if(null != ioe){
  //re-throw the exception to the caller
  throw ioe;
}

上記は非常に冗長ですが、適切に機能IOExceptionします。これは、開発者にとって関心のある情報が含まれている可能性が高いため、クローズ中の I/O よりもリソースの I/O 中を優先するためです。IOExceptionコードは何か問題が発生したときにもスローするため、統一された動作が得られます。

もっと良い方法があるかもしれませんが、アイデアはわかります。

理想的には、兄弟例外を格納できる新しい例外タイプを作成することで、2 つIOExceptionの を取得した場合に両方を格納できます。

于 2011-01-13T14:21:22.750 に答える
0

ライターを閉じる前に、flush を使用してみてください。クローズ時の例外の主な理由は、他のリソースがデータを使用していたか、ライター/リーダーがまったく開いていない可能性があることです。リソースを閉じる前に、リソースが開いているかどうかを確認してください。

于 2011-01-13T11:22:25.857 に答える
-2

ほとんどの場合、close()は実際にはIOExceptionをスローしません。InputStream.javaのコードは次のとおりです。

  public void close() throws IOException
  {
    // Do nothing
  }

RuntimeExceptionプログラムがネットワークリソースに接続した後でネットワークリソースを切断できるため、ネットワークリソースを閉じることによるエラーは、実際にはある種のエラーである必要があります。

Googleコード検索を使用したReader /WriterとStreamsのさまざまな実装の例をいくつか見ることができます。BufferedReaderもPipedReaderも実際にはIOExceptionをスローしないので、心配することなく、ほとんど安全になっていると思います。本当に心配な場合は、使用しているライブラリの実装をチェックして、例外について心配する必要があるかどうかを確認できます。

他の人が述べたように、IOExceptionについてはログに記録する以外に多くのことを行うことはできません。

結局のところ、try/catch blocks条項finallyではかなり醜いです。

編集:

さらに詳しく調べると、、、、、IOExceptionなどInterruptedIOExceptionのサブクラスSyncFailedExceptionObjectStreamException、それを継承するクラスが明らかになります。したがって、をキャッチするだけIOExceptionでは一般的すぎます。情報はさまざまなエラーからのものである可能性があるため、ログに記録する以外に情報をどう処理するかがわかりません。

編集2:

Urkは、入力としてを受け取るBufferedReaderため、悪い例でした。Readerに変更しましたInputStream.java

InputStreamただし、 <= FilterInputStream<= BufferedInputStream<= InputStreamReader(継承およびプライベートインスタンスを介して)の階層があり、すべてが。のclose()メソッドに到達しInputStreamます。

于 2011-01-13T11:35:24.673 に答える