finally
ブロックでスローされる例外を処理するエレガントな方法はありますか?
例えば:
try {
// Use the resource.
}
catch( Exception ex ) {
// Problem with the resource.
}
finally {
try{
resource.close();
}
catch( Exception ex ) {
// Could not close the resource?
}
}
try
ブロック内のcatch
/finally
をどのように回避しますか?
私は通常このようにします:
try {
// Use the resource.
} catch( Exception ex ) {
// Problem with the resource.
} finally {
// Put away the resource.
closeQuietly( resource );
}
他の場所:
protected void closeQuietly( Resource resource ) {
try {
if (resource != null) {
resource.close();
}
} catch( Exception ex ) {
log( "Exception during Resource.close()", ex );
}
}
通常、org.Apache.commons.io.IOUtils
のcloseQuietly
メソッドのいずれかを使用します。
public static void closeQuietly(OutputStream output) {
try {
if (output != null) {
output.close();
}
} catch (IOException ioe) {
// ignore
}
}
Java 7、およびresource
がAutoClosable
を実装している場合、これを行うことができます(例としてInputStreamを使用)):
try (InputStream resource = getInputStream()) {
// Use the resource.
}
catch( Exception ex ) {
// Problem with the resource.
}
ほぼ間違いなく、例外をバブルアップさせ、メソッド内から何もログに記録できない場合に役立つかもしれません(たとえば、ライブラリであるため、呼び出しコードに例外とロギングを処理させたい場合):
Resource resource = null;
boolean isSuccess = false;
try {
resource = Resource.create();
resource.use();
// Following line will only run if nothing above threw an exception.
isSuccess = true;
} finally {
if (resource != null) {
if (isSuccess) {
// let close throw the exception so it isn't swallowed.
resource.close();
} else {
try {
resource.close();
} catch (ResourceException ignore) {
// Just swallow this one because you don't want it
// to replace the one that came first (thrown above).
}
}
}
}
更新:私はこれをもう少し調べて、私よりもこれについて明確に考えている人からの素晴らしいブログ投稿を見つけました: http://illegalargumentexception.blogspot.com/2008/10/Java-how-not -to-make-mess-of-stream.html 彼はさらに一歩進んで、2つの例外を1つに結合します。
Java 7以降、finallyブロックのリソースを明示的に閉じる必要はなくなりました。代わりにtry-with-resources構文。try-with-resourcesステートメントは、1つ以上のリソースを宣言するtryステートメントです。リソースは、後で閉じる必要があるオブジェクトですtry-with-resourcesステートメントは、各リソースがステートメントの最後で閉じられるようにしますJava.io.Closeableを実装するすべてのオブジェクトを含むJava.lang.AutoCloseableを実装するオブジェクトは、リソースとして使用されます。
次のコードを想定します。
try( Connection con = null;
Statement stmt = con.createStatement();
Result rs= stmt.executeQuery(QUERY);)
{
count = rs.getInt(1);
}
例外が発生した場合、closeメソッドは、これら3つのリソースのそれぞれに対して、作成された順序とは逆の順序で呼び出されます。つまり、最初にResultSetmに対してcloseメソッドが呼び出され、次にConnectionオブジェクトに対して最後にStatementが呼び出されます。
また、closeメソッドが自動的に呼び出されたときに発生する例外はすべて抑制されることを知っておくことが重要です。これらの抑止された例外は、getsuppressed()メソッドで定義できますThrowableクラス。
ソース: https://docs.Oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
「最終」ブロックで発生する例外を無視するのは、一般的には 悪いアイデア それらの例外が何であり、それらがどのような条件を表すかを知らない限り。通常のtry/finally
使用パターン、try
ブロックは、外部コードが予期しない状態に物事を配置し、finally
ブロックは、それらの物事の状態を外部コードが予期するものに戻します。例外をキャッチする外部コードは通常、例外にもかかわらず、すべてがnormal
状態に復元されたことを期待します。たとえば、あるコードがトランザクションを開始してから2つのレコードを追加しようとするとします。 「最終」ブロックは「コミットされない場合のロールバック」操作を実行します。呼び出し元は、2番目の「追加」操作の実行中に例外が発生する準備ができている場合があり、そのような例外をキャッチすると、データベースはいずれかの操作が試行される前の状態になります。ただし、ロールバック中に2番目の例外が発生した場合、呼び出し側がデータベースの状態について何らかの仮定を行うと、悪いことが起こる可能性があります。ロールバックの失敗は メジャー 危機-単なる「レコードの追加に失敗しました」という例外を予期するコードによってキャッチされるべきではないもの。
私の個人的な傾向は、最終的にメソッドで発生する例外をキャッチし、それらを「CleanupFailedException」にラップして、そのような失敗が重大な問題を表し、そのような例外を軽度にキャッチしてはならないことです.
1つのソリューション、2つの例外が2つの異なるクラスである場合
try {
...
}
catch(package1.Exception err)
{
...
}
catch(package2.Exception err)
{
...
}
finally
{
}
しかし、この2回目のtry-catchを避けることができない場合があります。例えばストリームを閉じるために
InputStream in=null;
try
{
in= new FileInputStream("File.txt");
(..)// do something that might throw an exception during the analysis of the file, e.g. a SQL error
}
catch(SQLException err)
{
//handle exception
}
finally
{
//at the end, we close the file
if(in!=null) try { in.close();} catch(IOException err) { /* ignore */ }
}
十分に検討した結果、次のコードが最適であることがわかりました。
MyResource resource = null;
try {
resource = new MyResource();
resource.doSomethingFancy();
resource.close();
resource = null;
} finally {
closeQuietly(resource)
}
void closeQuietly(MyResource a) {
if (a!=null)
try {
a.close();
} catch (Exception e) {
//ignore
}
}
そのコードは以下を保証します:
なぜ追加のブロックを避けたいのですか? finallyブロックには例外をスローする可能性のある「通常の」操作が含まれているため、finallyブロックを完全に実行したい場合は例外をキャッチする必要があります。
Finallyブロックが例外をスローすることを期待しておらず、とにかく例外を処理する方法がわからない場合(スタックトレースをダンプするだけです)、例外が呼び出しスタックをバブルさせます(try-catchをfinallyから削除します)ブロック)。
入力を減らしたい場合は、finallyブロックでスローされたすべての例外をキャッチする「グローバルな」外部try-catchブロックを実装できます。
try {
try {
...
} catch (Exception ex) {
...
} finally {
...
}
try {
...
} catch (Exception ex) {
...
} finally {
...
}
try {
...
} catch (Exception ex) {
...
} finally {
...
}
} catch (Exception ex) {
...
}
try {
final Resource resource = acquire();
try {
use(resource);
} finally {
resource.release();
}
} catch (ResourceException exx) {
... sensible code ...
}
仕事完了。 nullテストはありません。シングルキャッチ、取得およびリリース例外を含む。もちろん、Execute Aroundイディオムを使用でき、リソースタイプごとに1回だけ記述する必要があります。
私は通常これを行います:
MyResource r = null;
try {
// use resource
} finally {
if( r != null ) try {
r.close();
} catch( ThatSpecificExceptionOnClose teoc ){}
}
理論的根拠:リソースの処理が完了し、唯一の問題がリソースを閉じることである場合、それについてできることはあまりありません。とにかくリソースを使い終わったら、スレッド全体を殺すことも意味がありません。
これは、少なくとも私にとっては、そのチェック済み例外を無視しても安全な場合の1つです。
今日まで、このイディオムを使用しても問題はありませんでした。
可能であれば、最初からエラー状態を回避するためにテストする必要があります。
try{...}
catch(NullArgumentException nae){...}
finally
{
//or if resource had some useful function that tells you its open use that
if (resource != null)
{
resource.Close();
resource = null;//just to be explicit about it was closed
}
}
また、回復できない例外のみをキャッチする必要があります。回復できない場合は、プログラムの最上位レベルに伝播します。エラー状態をテストできない場合、既に行ったようにコードをtry catchブロックで囲む必要があります(ただし、特定の予期されるエラーをキャッチすることをお勧めします)。
これを別のメソッドにリファクタリングできます...
public void RealDoSuff()
{
try
{ DoStuff(); }
catch
{ // resource.close failed or something really weird is going on
// like an OutOfMemoryException
}
}
private void DoStuff()
{
try
{}
catch
{
}
finally
{
if (resource != null)
{
resource.close();
}
}
}