finallyブロックは常にJavaで実行されますか?


2382

このコードを考えると、何があっも、finallyブロックが常に実行されると確信できsomething()ますか?

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("I don't know if this will get printed out");
}

473
そうでない場合は、キーワードにprobably代わりに名前を付ける必要があります。
正午



2
効果的なJavaは、そうでなければinformit.com/articles/article.aspx?p=1216151&seqNum=7
Binoy Babu

27
@BinoyBabu、ファイナライザ!= finally; finalizer == finalize()メソッド。
jaco0646

回答:


2699

はい、またはコードブロックのfinally実行後に呼び出されます。trycatch

finally呼び出されないのは次の場合のみです。

  1. あなたが呼び出す場合 System.exit()
  2. あなたが呼び出す場合 Runtime.getRuntime().halt(exitStatus)
  3. JVMが最初にクラッシュした場合
  4. JVMがtryor catchブロックで無限ループ(またはその他の割り込み不能で終了しないステートメント)に達した場合
  5. OSがJVMプロセスを強制終了した場合。例:kill -9 <pid>UNIX
  6. ホストシステムが停止した場合。例:電源障害、ハードウェアエラー、OSパニックなど
  7. 場合はfinally前のブロックは、デーモンスレッドとすべての他の非デーモンスレッド出口によって実行されようとしているfinallyと呼ばれています

44
実際にthread.stop()は、必ずしもfinallyブロックの実行が妨げられるわけではありません。
Piotr Findeisen 2011年

181
どのようにしていると言う程度finallyのブロックが呼び出されますtryブロック、および制御は次の文に渡されます。これは、無限ループを含むtryブロックと一致しているため、finallyブロックが実際に呼び出されることはありません。
Andrzej Doyle

9
ネストされたtry-catch-finallyブロックを使用する別のケースもあります
ruhungry

7
また、デーモンスレッドによって例外がスローされた場合、最終的にブロックは呼び出されません。
Amrish Pandey 2014

14
@BinoyBabu -ファイナライザについてです、finallyブロックではない
avmohan

567

コード例:

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

出力:

finally trumps return. 
0

18
参考:C#では、finally-clause のステートメントの置換がreturn 2;許可されていない(コンパイラエラー)以外は、動作は同じです。
Alexander Pacha 2013年

15
ここに注意すべき重要な詳細があります:stackoverflow.com/a/20363941/2684342
WoodenKitty

18
さらに、finallyブロック自体にreturnステートメントを追加して、以前の戻り値をオーバーライドすることもできます。これにより、未処理の例外も魔法のように破棄されます。その時点で、コードのリファクタリングを検討する必要があります。
Zyl

8
これは、最終的にトランプが戻ってきたことを実際に証明するものではありません。戻り値は、呼び出し元コードから出力されます。あまり証明されていないようです。
Trimtab 2016年

20
申し訳ありませんが、これは証明ではなくデモです。この例がすべてのJavaプラットフォームで常にこのように動作すること、および同様の例も常にこのように動作することを示すことができれば、それは証明にすぎません。
スティーブンC

391

また、これは悪い習慣ですが、finallyブロック内にreturnステートメントがある場合、通常のブロックからの他の戻りよりも優先されます。つまり、次のブロックはfalseを返します。

try { return true; } finally { return false; }

finallyブロックから例外をスローする場合も同様です。


95
これは本当に悪い習慣です。それが悪い理由の詳細については、stackoverflow.com / questions / 48088 /…を参照してください。
John Meagher、

23
同意した。finally {}内の戻りは、try {}でスローされた例外を無視します。怖い!
neu242 2008年

8
@ dominicbri7なぜあなたはそれがより良い実践だと思いますか?そして、関数/メソッドが無効であるとき、なぜそれが異なるのですか?
corsiKa 2011

8
同じ理由で、C ++コードでgotoを使用することはありません。複数のリターンがあると、読み取りが難しくなり、デバッグが難しくなります(もちろん、本当に単純なケースでは適用されません)。それは単なる個人的な好みだと思いますが、最終的にはどちらの方法を使用しても同じことを達成できます
dominicbri7

16
なんらかの例外的なケースが発生した場合、私は多くのリターンを使用する傾向があります。if(継続しない理由がある)returnのように;
iHearGeoff 2013

257

これは、Java言語仕様の公式の言葉です。

14.20.2。try-finallyおよびtry-catch-finallyの実行

ブロックを含むtryステートメントは、finally最初にtryブロックを実行することによって実行されます。次に選択肢があります:

  • tryブロックの実行が正常に完了した場合、[...]
  • Vtry原因でブロックの実行が突然完了しthrowた場合、[...]
  • try他の理由Rでブロックの実行が突然完了すると、finallyブロックが実行されます。次に選択肢があります:
    • finallyブロックが正常に完了すると、try理由Rのためにステートメントが突然完了します。
    • 場合はfinallyブロックが理由で中途完了S、その後、try突然理由のためのステートメントが完了Sおよびその理由Rは捨てられます)。

の仕様はreturn、これを実際に明示的にしています。

JLS 14.17 returnステートメント

ReturnStatement:
     return Expression(opt) ;

returnなしで文Expression の試み、それを含むメソッドまたはコンストラクタの呼び出し元に制御を転送します。

return付きステートメントExpression の試みを含むメソッドの呼び出し元に制御を移します。の値は、Expressionメソッド呼び出しの値になります。

前述の説明は、「言う試み転送制御に」だけでなく「転送制御を任意ある場合ので、」tryメソッドやコンストラクタ内のステートメントtryのブロック含有return次いで、任意の文を、finallyそれらの句try文が順に実行され、最も外側に最も内側の、コントロールがメソッドまたはコンストラクタの呼び出し側に転送される前。finally文節が突然完了すると、returnステートメントによって開始された制御の移行が中断される可能性があります。


163

他の応答に加えて、「最終的に」は、try..catchブロックによって例外/返された値をオーバーライドする権利があることを指摘することが重要です。たとえば、次のコードは12を返します。

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

同様に、次のメソッドは例外をスローしません。

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

次のメソッドはそれをスローしますが:

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}

63
真ん中のケースが、finallyブロック内にreturnステートメントを置くことが絶対に恐ろしい理由であることに注意してください(Throwableを隠す可能性があります)。
Dimitris Andreou

2
抑圧されたくないのですOutOfMemoryErrorか?;)
RecursiveExceptionException

私はそれをテストしました、そしてそれはそのようなエラーを抑制します(yipes!)。また、コンパイルすると警告が表示されます(yay!)。そして、あなたは戻り変数を定義してから使用することによってそれを回避することができreturn retVal た後、finallyもちろんそれはコードがそうでなければ意味がありませんので、あなたが他のいくつかの例外を抑制していることを前提としていますが、ブロック。
Maarten Bodewes

120

上記の例を少し変更して試しましたが、

public static void main(final String[] args) {
    System.out.println(test());
}

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

上記のコードは出力します:

ついにトランプが帰ってきた。
2

場合これはreturn i;実行されi、この後に値2を有するfinally12が割り当てられている場合、ブロックが実行されi、その後、System.outアウト実行されます。

finallyブロックを実行した後try、このreturnステートメントは再度実行されないため、ブロックは12ではなく2を返します。

このコードをEclipseでデバッグするとSystem.outfinallyblockの実行後にblockのreturnステートメントがtry再度実行されたように感じるでしょう。しかし、そうではありません。単に値2を返します。


10
この例は素晴らしく、最終的に関連する数十のスレッドで言及されていないものを追加します。ほとんどの開発者はこれを知っていると思います。
2016

4
どのような場合はi、プリミティブが、Integerオブジェクトではなかったです。
Yamcha 2016

私はこの事件を理解するのに苦労しています。docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17は、「式を含むreturnステートメントは、メソッドの呼び出し元または次を含むラムダ本体に制御を転送しようとしますit ....式の評価が正常に完了し、値Vが生成される場合。 "このステートメントから推測できたのは、値Vを評価した後、returnが式を再度評価しないようです。それが、変更された理由です。戻り値には影響しません。修正してください。
meexplorer 2016年

しかし、私はこれに関する証拠を見つけることができませんでした。returnは式を再度評価しないと述べられています。
meexplorer 2016年

1
@meexplorerは少し遅れますが、JLS 14.20.2で説明されています。try-finallyとtry-catch-finallyの実行 -やや複雑、14.17。returnステートメントも読む必要があります
user85421

117

ケビンの答えの詳細は次のとおりです。返される式はfinally、後で返された場合でも、の前に評価されることを知っておくことが重要です。

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int printX() {
    System.out.println("X");
    return 0;
}

public static int test() {
    try {
        return printX();
    }
    finally {
        System.out.println("finally trumps return... sort of");
    }
}

出力:

X
finally trumps return... sort of
0

8
知っておくべき重要なこと。
Aminadav Glickshtein

知っておくと便利です。実際にreturnの値を返すのが後のようfinallyです。戻り値の計算(printX()ここ)はまだその前です。
アルバート

3つの「リターン」ポイントすべての良い例です。
radistao

いいえ。上記のコードは交換する必要がありますSystem.out.println("finally trumps return... sort of");System.out.print("finally trumps return in try"); return 42;
Pacerier

54

これがfinallyブロックの全体的な考え方です。もちろん、特に戻ってくるためにスキップされる可能性のあるクリーンアップを確実に実行できます。

最後に、tryブロックで何が発生するかに関係なく呼び出されます(呼び出すか、Java仮想マシンが他の理由で起動しない限りSystem.exit(int))。


それは非常に弱い答えです。stackoverflow.com/a/65049/715269
Gangnus

42

これについて論理的に考える方法は次のとおりです。

  1. finallyブロックに配置されたコードは、tryブロック内で発生したものを実行する必要があります
  2. したがって、tryブロックのコードが値を返すか、例外をスローしようとすると、finallyブロックが実行されるまで、アイテムは「シェルフ」に配置されます
  3. finallyブロックのコードは(定義により)高い優先度を持っているため、好きなものを返したりスローしたりできます。その場合、「棚に」残ったものはすべて破棄されます。
  4. これの唯一の例外は、たとえば「System.exit」によって、tryブロック中にVMが完全にシャットダウンした場合です。

10
これは単に「それについて考える論理的な方法」ですか、それとも本当に、finallyブロックが仕様に従って動作するように意図されているのですか?ここでは、Sunのリソースへのリンクが非常に興味深いでしょう。
matias

21

最終的には、プログラムの異常終了(System.exit(0)..の呼び出しなど)がない限り、常に実行されます。だから、あなたのsysoutは印刷されます



18

finallyブロックは、JVMクラッシュまたはへの呼び出しの結果としてプログラムの異常終了がない限り、常に実行されSystem.exit(0)ます。

さらに、finallyブロック内から返された値は、finallyブロックの実行前に返された値をオーバーライドするため、try finallyを使用する場合はすべての出口点をチェックするように注意してください。


18

いいえ、常に1つの例外が発生するわけではありません/// System.exit(0); finallyブロックの前では、finallyは実行されません。

  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}

そして、それがSystem.exit()を呼び出してはいけない理由の1つです...
Franz D.

13

最後に、常に実行されますが、それが全体の要点です。それは、コードがリターンの後に現れたからといって、それがその実装方法であるとは限らないからです。Javaランタイムは、tryブロックを終了するときにこのコードを実行する責任があります。

たとえば、次の場合:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

ランタイムは次のようなものを生成します:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

キャッチされない例外がスローされると、finallyブロックが実行され、例外は伝播し続けます。


11

これは、iの値を12として割り当てたが、iの値を関数に返さなかったためです。正しいコードは次のとおりです。

public static int test() {
    int i = 0;
    try {
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
        return i;
    }
}

10

なぜなら、あなたが呼び出すSystem.exit()(またはスレッドがクラッシュする)までは、finallyブロックが常に呼び出されるからです。


10

簡潔には、公式のJavaドキュメント(クリックでここでは)、次のことが書かれています-

tryまたはcatchコードの実行中にJVMが終了すると、finallyブロックが実行されない場合があります。同様に、tryまたはcatchコードを実行しているスレッドが中断または終了すると、アプリケーション全体が継続していても、finallyブロックが実行されない場合があります。


10

答えは簡単ですYES

入力:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e){
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

出力:

catch
finally

1
答えは簡単です。
Christophe Roussy 2017

1
@ChristopheRoussyどうやって?説明していただけますか?
会う

1
受け入れられた回答を読んでください。元の質問は「常に実行されますか」であり、常に実行されるわけではありません。あなたのケースではそれはしますが、これは元の質問に答えず、初心者を誤解させるかもしれません。
Christophe Roussy

その後、その場合は実行されませんか?
会う

他の回答で言及されているすべてのケースで、1000以上の賛成投票で承認された回答を参照してください。
Christophe Roussy

9

はい、呼ばれます。以上が、finallyキーワードを持つことの要点です。try / catchブロックから飛び出すことが、finallyブロックをスキップするだけの場合、それは、System.out.printlnをtry / catchの外に置くのと同じでした。


9

はい、最終的にブロックは常に実行されます。開発者のほとんどは、データベース接続、結果セットオブジェクト、ステートメントオブジェクトを閉じるときにこのブロックを使用し、Java hibernateを使用してトランザクションをロールバックします。


9

常にではない

Java言語仕様では、方法について説明try- catch- finallytry- catchブロックがで働く14.20.2
それがあることを指定していない場所ではfinally、ブロックが常に実行されます。しかし、すべてのケースのためにtry- catch- finallytry- finallyブロックは、それは前に完了することを指定しない完成finally実行する必要があります。

try {
  CODE inside the try block
}
finally {
  FIN code inside finally block
}
NEXT code executed after the try-finally block (may be in a different method).

JLSはCODEの後にFINが実行されることを保証しません。JLSは、CODEおよびNEXTが実行される場合、FINが常にCODEの後かつNEXTの前に実行されることを保証します。

JLSは、finallyブロックがtryブロックの後に常に実行されることを保証しないのはなぜですか?それは不可能だからです。tryブロックが完了した後、ブロックが実行される前に、JVMが異常終了(強制終了、クラッシュ、電源オフ)することはほとんどありませんfinally。これを回避するためにJLSができることは何もありません。

したがって、適切な動作のためfinallyに、tryブロックの完了後に常に実行されているブロックに依存するソフトウェアには、バグが含まれています。

returntryブロック内の指示はこの問題には関係ありません。実行後のコードに達した場合try- catch- finallyことが保証されているfinallyブロックは、の有無にかかわらず、以前に実行されているであろうreturn内部の命令をtryブロック。


8

はい、そうです。それ以外の場合はSystem.exit()が呼び出されるか、JVMがクラッシュしない限り、tryまたはcatchブロックで何が発生しても。ブロックにreturnステートメントがある場合、最終的にそのreturnステートメントの前に実行されます。



8

@vibhashの回答に追加すると、以下のような変更可能なオブジェクトの場合に何が起こるかが説明されます。

public static void main(String[] args) {
    System.out.println(test().toString());
}

public static StringBuffer test() {
    StringBuffer s = new StringBuffer();
    try {
        s.append("sb");
        return s;
    } finally {
        s.append("updated ");
    }
}

出力されます

sbupdated 

Java 1.8.162以降、これは出力ではありません。
サム

8

私はこれを試しました、それはシングルスレッドです。

public static void main(String args[]) throws Exception {
    Object obj = new Object();
    try {
        synchronized (obj) {
            obj.wait();
            System.out.println("after wait()");
        }
    } catch (Exception ignored) {
    } finally {
        System.out.println("finally");
    }
}

main Threadなりwait、永遠の状態ので、finally呼び出されることはありません

そのため、コンソール出力は次のようになりませんprint String:後wait()またはfinally

@Stephen Cと同意し、上記の例はここで言及する3番目のケースの1つです

以下のコードで、このような無限ループの可能性をさらに追加します。

// import java.util.concurrent.Semaphore;

public static void main(String[] args) {
    try {
        // Thread.sleep(Long.MAX_VALUE);
        // Thread.currentThread().join();
        // new Semaphore(0).acquire();
        // while (true){}
        System.out.println("after sleep join semaphore exit infinite while loop");
    } catch (Exception ignored) {
    } finally {
        System.out.println("finally");
    }
}

ケース2:JVMが最初にクラッシュする場合

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public static void main(String args[]) {
    try {
        unsafeMethod();
        //Runtime.getRuntime().halt(123);
        System.out.println("After Jvm Crash!");
    } catch (Exception e) {
    } finally {
        System.out.println("finally");
    }
}

private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    Unsafe unsafe = (Unsafe) f.get(null);
    unsafe.putAddress(0, 0);
}

参照:JVMをどのようにクラッシュさせますか?

ケース6:finallyブロックがデーモンによって実行されThread、他のすべての非デーモンThreads出口finallyが呼び出される場合。

public static void main(String args[]) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                printThreads("Daemon Thread printing");
                // just to ensure this thread will live longer than main thread
                Thread.sleep(10000);
            } catch (Exception e) {
            } finally {
                System.out.println("finally");
            }
        }
    };
    Thread daemonThread = new Thread(runnable);
    daemonThread.setDaemon(Boolean.TRUE);
    daemonThread.setName("My Daemon Thread");
    daemonThread.start();
    printThreads("main Thread Printing");
}

private static synchronized void printThreads(String str) {
    System.out.println(str);
    int threadCount = 0;
    Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
    for (Thread t : threadSet) {
        if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
            System.out.println("Thread :" + t + ":" + "state:" + t.getState());
            ++threadCount;
        }
    }
    System.out.println("Thread count started by Main thread:" + threadCount);
    System.out.println("-------------------------------------------------");
}

出力:「デーモンスレッド」の「最終的にブロック」が実行されなかったことを意味する「最終的に」は出力されません

main Thread Printing  
Thread :Thread[My Daemon Thread,5,main]:state:BLOCKED  
Thread :Thread[main,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE   
Thread count started by Main thread:3  
-------------------------------------------------  
Daemon Thread printing  
Thread :Thread[My Daemon Thread,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE  
Thread count started by Main thread:2  
-------------------------------------------------  

Process finished with exit code 0

4
受け入れられた答えを見てください。これは「無限ループ」の端的なケースです。
スティーブンC

8

次のプログラムを検討してください。

public class SomeTest {

    private static StringBuilder sb = new StringBuilder();

    public static void main(String args[]) {

        System.out.println(someString());
        System.out.println("---AGAIN---");
        System.out.println(someString());
        System.out.println("---PRINT THE RESULT---");
        System.out.println(sb.toString());
    }

    private static String someString() {

        try {
            sb.append("-abc-");
            return sb.toString();

        } finally {
            sb.append("xyz");
        }
    }
}

Java 1.8.162以降、上記のコードブロックは次の出力を提供します。

-abc-
---AGAIN---
-abc-xyz-abc-
---PRINT THE RESULT---
-abc-xyz-abc-xyz

つまりfinally、次のコードのように、オブジェクトを解放するためにを使用することをお勧めします。

private static String someString() {

    StringBuilder sb = new StringBuilder();

    try {
        sb.append("abc");
        return sb.toString();

    } finally {
        sb = null; // Just an example, but you can close streams or DB connections this way.
    }
}

sb.setLength(0)ようやく入ってはいけませんか?
user7294900 2018

sb.setLength(0)は、StringBufferのデータを空にするだけです。したがって、sb = nullは、オブジェクトと参照の関連付けを解除します。
サム

「xyz」は出力に2回出力されるべきではありませんか?関数が2回呼び出されたので、「最終的に」が1回だけだったのはなぜですか。
fresko

2
これは良い習慣ではありません。最後にsb = null;、不要なコードを追加するだけでブロックします。finallyブロックがデータベース接続などのリソースを解放するのに適した場所であることを理解していると思いますが、この例では新規参入者を混乱させる可能性があることを覚えておいてください。
Roc Boronat

1
@サミムありがとう、私は行を追加しました、 System.out.println("---AGAIN2---"); System.out.println(sb);そしてそれは今より明確です。実際のところ、出力はあなたの論文に対するものでした:p私もあなたの回答に追加しましたが、編集はモデレーターまたはそのような誰かによって受け入れられなければなりません。それ以外の場合は追加できます
フレスコ

7

これは実際にはどの言語でも当てはまります。最後に、メソッド本体のどこにreturnがあっても、returnステートメントの前に常に実行されます。そうでなければ、finallyブロックはあまり意味がありません。


7

tryブロックのreturnを最終的に置き換える際のreturnに加えて、例外についても同じことが言えます。例外をスローするfinallyブロックは、tryブロック内からスローされたreturnまたはexceptionを置き換えます。


7

finally 実行され、それは確かです。

finally 以下の場合は実行されません。

ケース1:

あなたが実行しているときSystem.exit()

ケース2:

JVM /スレッドがクラッシュしたとき。

ケース3:

実行が手動で途中で停止したとき。


6
  1. 最後に、ブロックは常に実行されます。System.exit()ステートメントが存在しない限り 最後のブロックの最初のステートメント)。
  2. もしにSystem.exit() finallyブロックが実行され得ることはありませんし、制御がfinallyブロックから出てくる最初の文です。System.exit()ステートメントが最終的にブロックに入るまで、そのステートメントが最終的にブロックを実行し、System.exit()が表示されると、制御力が最終的にブロックから完全に抜けます。

1
これは何度も回答されているので、あなたの回答はどの新しい情報を追加しますか?
トム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.