2035

このコードを考えると、絶対確実あれ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.");
}


  • これを自分でテストした後でも、あなたは何を知っているだけですきみのJava / JVMのインプリメンテーションまたはバージョンは機能し、コードが何を意味するのかわからないすべき(標準に従って)するので、問題は有効なままです。これらの側面がさまざまな回答の中で解決されていることを嬉しく思います。 - Rhubbarb
  • ではない常に - Boann
  • 効果的なJavaはそれ以外の場合informit.com/articles/article.aspx?p=1216151&seqNum=7 - Binoy Babu
  • @BinoyBabu、ファイナライザ!=finally;ファイナライザ== thefinalize()方法。 - jaco0646
  • @LordFarquaad ;-)それは確かに常に保証されています。 - MC Emperor

30 답변


2267

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

唯一の回finally呼ばれることはありません:

  1. 起動した場合System.exit();
  2. JVMが最初にクラッシュした場合
  3. JVMがで無限ループ(またはその他の中断不可能な非終了ステートメント)に到達した場合tryまたはcatchブロック;
  4. OSがJVMプロセスを強制終了した場合例えばUNIXでは "kill -9"です。
  5. ホストシステムが停止した場合例えば停電、ハードウェアエラー、OSパニックなど。
  6. finallyがデーモンスレッドによって実行され、他のすべての非デーモンスレッドがfinallyが呼び出される前に終了する場合。


  • 実はthread.stop()必ずしも防ぐわけではないfinally実行されないようにブロックします。 - Piotr Findeisen
  • それはどうですか?finallyブロックが呼び出されます後にtryブロックし、制御は以下のステートメントに渡されます。これは、無限ループを含むtryブロックと一致しているため、finallyブロックは実際には呼び出されません。 - Andrzej Doyle
  • 入れ子にして使用するとき、別のケースもありますtry-catch-finallyブロック - ruhungry
  • また、デーモンスレッドによって例外が送出された場合、最後にblockは呼び出されません。 - Amrish Pandey
  • @BinoyBabu - ファイナライザについての話、最後にブロックではない - avmohan

472

コード例:

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


  • FYI:C#では、振る舞いはステートメント内のステートメントを置き換えるという事実を除いて同一です。finally〜とreturn 2;許可されていません(Compiler-Error)。 - Alexander Pacha
  • 注意すべき重要な詳細は次のとおりです。stackoverflow.com/a/20363941/2684342 - WoodenKitty
  • finallyブロック自体にreturn文を追加することもできます。これにより、前の戻り値が上書きされます。これは未処理の例外も魔法のように捨てます。その時点で、あなたはあなたのコードをリファクタリングすることを考えるべきです。 - Zyl
  • それは本当に最終的に切り札が戻ってくることを証明していません。戻り値は呼び出し元コードから出力されます。あまり証明できないようです。 - Trimtab
  • 申し訳ありませんが、これは証明ではなくデモです。この例がすべてのJavaプラットフォームで常にこのように動作すること、および同様の例が常にこのように動作することを示すことができれば、それは証明にすぎません。 - Stephen C

339

また、これは悪い習慣ですが、finallyブロック内にreturn文があると、通常のブロックからの他の戻り値よりも優先されます。つまり、次のブロックはfalseを返します。

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

finallyブロックから例外をスローするのと同じことです。


  • これは本当に悪い習慣です。見るstackoverflow.com/questions/48088/…なぜそれが悪いのかについては、こちらをご覧ください。 - John Meagher
  • 同意した。 finally {}内のリターンは、try {}で送出された例外を無視します。怖い! - neu242
  • @ dominicbri7なぜこれがより良いやり方だと思いますか?そして関数/メソッドが無効なときになぜ違うのでしょうか。 - corsiKa
  • 同じ理由から、C ++コードではgotoを使用しないでください。複数回返品すると、読みにくくなり、デバッグが難しくなります(もちろん、単純な場合は適用されません)。私は、個人的な好みであり、最終的にはどちらの方法でも同じことができると思います。 - dominicbri7
  • ある種の例外的なケースが起こるとき、私は多くの収益を使う傾向があります。 (継続しない理由があるのなら)returnのように。 - iHearGeoff

237

これがJava言語仕様からの公式の言葉です。

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

Atryのステートメントfinallyブロックは最初にtryブロック。それから選択があります:

  • の実行の場合tryブロックは正常に完了します、[...]
  • の実行の場合tryブロックが原因で突然完了するthrow値のV、[...]
  • の実行の場合tryブロックは他の何らかの理由で突然完了しますR、 そうしてfinallyブロックが実行されます。それから選択があります:
    • finallyブロックが正常に完了すると、try文は理由で突然完了しますR
    • あればfinallyブロックは理由のために突然完了しますS、 そうしてtry文は理由で突然完了しますSそして理由R破棄されます

の仕様return実際にこれを明示的にする:

JLS 14.17 returnステートメント

ReturnStatement:
     return Expression(opt) ;

AreturnnoのあるステートメントExpression 試みそれを含むメソッドまたはコンストラクタの呼び出し側に制御を移す。

AreturnステートメントExpression 試みそれを含むメソッドの呼び出し元に制御を移す。の値Expressionメソッド呼び出しの値になります。

前述の説明は「試み制御を転送する「ただではなく」制御を転送する「あればtryメソッドまたはコンストラクタ内のステートメントtryブロックにはreturn文、それからanyfinallyそれらの節try制御がメソッドまたはコンストラクターの呼び出し側に渡される前に、ステートメントは最も内側から外側の順序で実行されます。の突然の完了finally条項によって開始された制御の移管が中断される可能性があります。returnステートメント。


142

他の応答に加えて、 'finally'が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();
    }
}


  • 真ん中のケースが、finallyブロック内にreturn文を持つことが絶対に恐ろしい理由であることに注意する必要があります(Throwableを隠す可能性があります)。 - Dimitris Andreou

97

上記の例を少し修正してみました。

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.outoutが実行されます。

実行後finallyブロックするtryこのreturn文は再度実行されないため、blockは12を返すのではなく2を返します。

あなたがEclipseでこのコードをデバッグするならば、あなたは実行した後にそのように感じるでしょうSystem.outfinallyブロックするreturnの声明tryブロックは再度実行されます。しかし、そうではありません。単に値2を返します。


  • この例は素晴らしいです、それは最後に関連した数十のスレッドで言及されていない何かを追加します。これを知っている開発者はほとんどいないと思います。 - HopefullyHelpful
  • 仮にiプリミティブではなく、Integerオブジェクトです。 - Yamcha
  • 私はこの事件を理解するのに苦労しています。docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17"式を含むreturn文は、それを含むメソッドまたはラムダ本体の呼び出し元に制御を移そうとします。式の評価が正常に完了した場合は、値Vを生成します。このステートメントから推測できるのは、return Vは値Vを評価した後で式を再度評価しないように思われる、なぜ変更したのが返された値に影響を与えないのか、訂正してください。 - meexplorer
  • しかし、これに関する証明は見つかりませんでした。ここで、returnは式を再度評価しないことに言及しています。 - meexplorer
  • @meexplorerが少し遅くなりましたが、で説明されていますJLS 14.20.2。 try-finallyおよびtry-catch-finallyの実行 - 少し複雑な表現14.17。リターンステートメントまた読む必要があります - Carlos Heuberger

89

これはの詳細ですケビンの答え。返される式が前に評価されることを知っておくことは重要です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



48

それがfinallyブロックの全体的な考え方です。それはあなたがあなたが他のものの間で、とりわけ戻るので、あなたがそうでなければスキップされるかもしれないクリーンアップをすることをあなたに確かにさせます。

最後に呼ばれる何があってもtryブロック内(ない限りあなたが呼ぶSystem.exit(int)またはJava Virtual Machineが他の理由で起動します。


33

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

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


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

16

また、やっと戻ってくると、例外がすべて捨てられます。http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html


15

(System.exit(0)を呼び出すように)プログラムの異常終了がない限り、finallyは常に実行されます。だから、あなたのSYSOUTが表示されます


15

いいえ、必ずしも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.

14

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

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


8

それがリターンの後にコードに表示されるという理由だけで、最後に常に実行されるのがポイントです。それはそれが実装される方法であることを意味するものではありません。 Javaランタイムは、終了時にこのコードを実行する責任があります。tryブロック。

たとえば、次のようになっているとします。

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

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

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

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


8

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

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


7

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


7

簡潔に言えば、公式のJavaドキュメンテーション(クリックしてここに)、それが書かれている -

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


7

答えは簡単はい

入力:

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


  • 答えは簡単NOです。 - Christophe Roussy
  • @ChristopheRoussyどうですか?説明してもらえますか? - Meet
  • 承認された回答を読むと、元の質問は「'それは常に実行されますか?」です。そしてそれは常にではないでしょう。あなたの場合はそうなるでしょうが、これは元の質問には答えず、誤解を招くような初心者でさえあるかもしれません。 - Christophe Roussy
  • それならどの場合それは実行されませんか? - Meet
  • 他の回答で言及されているすべてのケースでは、1000以上の投票で承認された回答を参照してください。 - Christophe Roussy

6

はい、それは呼ばれるでしょう。それがfinallyキーワードを持つことの全体的なポイントです。 try / catchブロックから飛び出してfinallyブロックを飛ばすことができれば、System.out.printlnをtry / catchの外側に置くのと同じことになります。


6

はい、できます。他の方法でSystem.exit()が呼び出されたりJVMがクラッシュしたりしない限り、tryまたはcatchブロックで何が起きても関係ありません。ブロック内にreturnステートメントがある場合は、そのreturnステートメントの前に最後に実行されます。


6

はいそうでしょう。 JVMが終了またはクラッシュしない場合のみ


6

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


5

次のプログラムを考えてください。

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());
    }

    private static String someString() {

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

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

Java 1.8.162以降、上記のコードブロックは次のように出力されます。

-abc-
---AGAIN---
-abc-xyz-abc-

これは、finallyオブジェクトを解放することは、次のコードのように良い習慣です。

private static String someString() {

    StringBuilder sb = new StringBuilder();

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

    } finally {
        sb = null;
    }
}


  • しないでくださいsb.setLength(0)最後に? - user7294900
  • sb.setLength(0)はStringBufferのデータを空にするだけです。そのため、sb = nullは、オブジェクトを参照から切り離します。 - Samim

4

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


  • すべての言語が持っているわけではありませんfinallyそもそも… - glglgl

4

なぜならファイナルはあなたが持っているどんな場合でも常に呼ばれるからです。あなたは例外を持っていない、それはまだ呼ばれている、例外をキャッチ、それはまだ呼ばれている


4

通常の実行の過程でこれを考慮してください(すなわち、例外がスローされることはありません)。


4

例外が発生した場合は、最後に実行されます。例外がスローされない場合は、最後に実行されます。例外が発生した場合は、最後に実行されます。例外が捕捉されない場合は、最後に実行されます。

実行されないのは、JVMが終了したときだけです。


4

tryブロックの前に例外が発生した場合、最後にblockは常に実行されません。


4

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

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

ケース1:

実行しているときSystem.exit()

ケース2:

あなたのJVM / Threadがクラッシュしたとき。

ケース3:

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



4

はい、そうです制御ステートメントなし防ぐことができますfinally実行されてから。

これは参考例で、すべてのコードブロックが実行されます。

| x | Current result | Code 
|---|----------------|------ - - -
|   |                |     
|   |                | public static int finallyTest() {
| 3 |                |     int x = 3;
|   |                |     try {
|   |                |        try {
| 4 |                |             x++;
| 4 | return 4       |             return x;
|   |                |         } finally {
| 3 |                |             x--;
| 3 | throw          |             throw new RuntimeException("Ahh!");
|   |                |         }
|   |                |     } catch (RuntimeException e) {
| 4 | return 4       |         return ++x;
|   |                |     } finally {
| 3 |                |         x--;
|   |                |     }
|   |                | }
|   |                |
|---|----------------|------ - - -
|   | Result: 4      |

下記の変形では、return x;スキップされます。結果はまだ4

public static int finallyTest() {
    int x = 3;
    try {
        try {
            x++;
            if (true) throw new RuntimeException("Ahh!");
            return x; // skipped
        } finally {
            x--;
        }
    } catch (RuntimeException e) {
        return ++x;
    } finally {
        x--;
    }
}

参照は、もちろん、それらのステータスを追跡します。この例では、参照を返します。value = 4

static class IntRef { public int value; }
public static IntRef finallyTest() {
    IntRef x = new IntRef();
    x.value = 3;
    try {
        return x;
    } finally {
        x.value++; // will be tracked even after return
    }
}

リンクされた質問


関連する質問

最近の質問