finallyは必ずしも実行されるわけではありません
5977 ワード
ネットで見た投稿は、基本的にはfinallyが必ず実行するということで、最初は私もずっとそう思っていました.しかし、私はこの内容をテストして、いくつかの文章を見て、発見しました. finallyは必ず実行するわけではありません!
1、結論
finallyは必ずしも実行されるとは限らず、finallyに対応するtryコードブロックが実行される場合にのみfinallyが実行されます!!
2、実例と分析
2,1 finallyが実行されていない場合
次の例のコードにはfinallyコードブロックがありますが、結果としてfinallyのコードは実行されず、内容は印刷されません.
2,2 finallyの実行原理
finally文ブロックは、転送文を制御する前に実行されるべきであり、制御転送文にはreturnのほかにbreakとcontinueがある.またthrow文も制御遷移文に属する.return、throw、break、continueはいずれも制御遷移文であるが、それらの間には違いがある.ここでreturnとthrowはプログラム制御権を呼び出し者(invoker)に渡し、breakとcontinueの制御権は現在の方法内で移行する.
実際、JVMはfinally文ブロックをサブルーチンとしてtry文ブロックまたはcatch文ブロックの制御遷移文の前に直接挿入します.しかし、subroutine(すなわちfinally文ブロック)を実行する前にtryまたはcatch文ブロックがローカル変数テーブル(Local Variable Table)に戻り値を保持するという無視できない要素もあります.subroutineの実行が完了したら、保持された戻り値をオペランドスタックに復元し、returnまたはthrow文を使用してメソッドの呼び出し元(invoker)に返します.
前にreturn、throw、break、continueの違いについて述べたように、このルール(戻り値を保持)ではreturn文とthrow文にのみ適用され、break文とcontinue文には適用されません.戻り値がないからです.
以上の説明では、finally文にreturn文が戻り値を上書きしていない場合、元の戻り値はfinallyの変更によって変更される可能性があり、変更されない可能性があります.
finallyが戻り値を変更していない場合
ここからも説明しますが、finallyでは コードブロックの前にreturnがあります そしてfinally 実行する場合
ステップ1:returnは1回実行し、ローカル変数テーブル(Local Variable Table)に戻り値を保持します.
ステップ2:finallyコードブロックの内容を実行します.
ステップ3:finallyコードブロックを実行した後、再び最終的なreturnに来て、そのメソッドの呼び出し者(invoker)に返します.
finally 戻り値を変更
最初のステップでreturn mapを初めて実行した場合、map タブで行います. valueはTRYです.JVMでは、値mapが返されます ローカル変数テーブルに保持されます.
ステップ2ではfinallyコードブロックを実行し、まず変更します. map キーをKEYにする 値はTRY キーをKEYに変更 値はFINALLY、map の参照をnullに設定します.説明するのは、実際に作成されたMapオブジェクトは、finallyではなくメモリに存在します(スタック内にあります). 最後にmapを の参照はnullに設定されていますが、スタック内のMapオブジェクトの内容には影響しません.このときスタック内のMapオブジェクトのvalue やはりFINALLYです.
ステップ3 finally コードブロックの実行が終了してからreturn mapを実行すると、以前に保存していたmapが取り出されるので、ここのmapはnullではなく、スタック内のMapオブジェクトの内容を指しています.やはり注意しなければならないのは、ここではMapオブジェクトを返すことです.Stringタイプの変数を返すのではなく、少し特殊です.Stringタイプの変数を返すのではなく、StringだけならTRYを返すべきです.ここではKEYに基づいています. をクリックして対応する値を取得し、スタック内のMapオブジェクトでキーに基づいてKEY 得られた値はFINALLYです.
異常がある場合
まとめ
もしtry 文は実行されませんでした(tryなど). 前に異常を投げ出したり戻したり)、tryで システム.out(0)が呼び出された場合、JVMが終了しました. は実行されません.
finally文ブロックは、tryまたはcatchまたはfinallyまたは他の場所(最後の行にreturnがあるなど)のreturn文の前に実行されます.次に、catchがある場合とない場合について説明します. 1,catchがある場合、まず異常がある場合にcatchを実行する. a、catchで異常を投げ出すとfinallyが先に実行されます. b,catchにreturnがあると最終結果はcatchにおけるreturnとなり, c,ただし同時にfinallyにreturnがある場合は,異常の有無にかかわらず最終結果はfinallyにおけるreturnである.異常がなければ実行されません catch. 2,catchがない場合,tryとfinallyのみである.異常があるかどうかにかかわらずcatchは自然に実行されません.catchコードがないからです. a,異常がある場合、 tryコードの前に異常が発生した場合(キャプチャされていない)、異常箇所の後は実行されず、finallyも実行されません (すなわち、finallyに対応するtry文ブロックが実行された場合にのみ、finally文ブロックが実行される). 異常がtryに現れるとfinallyが実行されます. b,異常がなければ、 finallyにreturnがある場合、最終結果はfinallyのreturnである. finallyにreturnがない場合、他のreturnを最終結果とします.
参考内容
1,Javaにおけるfinally文ブロックの試行判別についてhttps://www.ibm.com/developerworks/cn/java/j-lo-finally/index.html
2 Java finally文はreturnの前に実行されるのか、それとも後に実行されるのか.https://zhuanlan.zhihu.com/p/85377954
1、結論
finallyは必ずしも実行されるとは限らず、finallyに対応するtryコードブロックが実行される場合にのみfinallyが実行されます!!
2、実例と分析
2,1 finallyが実行されていない場合
次の例のコードにはfinallyコードブロックがありますが、結果としてfinallyのコードは実行されず、内容は印刷されません.
public static int test() {
int i = 1;
System.out.println("test");
//
i = i / 0;
try {
// i = i / 0 try ,finally
// i = i / 0;
System.out.println("try code");
} finally {
System.out.println("finally code");
}
}
上記のようにtryコードブロックの前に異常が発生しました.その後のコードは実行されず、finallyは自然に実行されません.最初の結論では、finallyは必ずしも実行されるとは限らず、finallyに対応するtryコードブロックが実行された場合にのみfinallyが実行される!!コード内の異常はtryコードの前に発生し、tryコードブロックが実行されず、最終的にfinallyも実行されない.2,2 finallyの実行原理
finally文ブロックは、転送文を制御する前に実行されるべきであり、制御転送文にはreturnのほかにbreakとcontinueがある.またthrow文も制御遷移文に属する.return、throw、break、continueはいずれも制御遷移文であるが、それらの間には違いがある.ここでreturnとthrowはプログラム制御権を呼び出し者(invoker)に渡し、breakとcontinueの制御権は現在の方法内で移行する.
実際、JVMはfinally文ブロックをサブルーチンとしてtry文ブロックまたはcatch文ブロックの制御遷移文の前に直接挿入します.しかし、subroutine(すなわちfinally文ブロック)を実行する前にtryまたはcatch文ブロックがローカル変数テーブル(Local Variable Table)に戻り値を保持するという無視できない要素もあります.subroutineの実行が完了したら、保持された戻り値をオペランドスタックに復元し、returnまたはthrow文を使用してメソッドの呼び出し元(invoker)に返します.
前にreturn、throw、break、continueの違いについて述べたように、このルール(戻り値を保持)ではreturn文とthrow文にのみ適用され、break文とcontinue文には適用されません.戻り値がないからです.
以上の説明では、finally文にreturn文が戻り値を上書きしていない場合、元の戻り値はfinallyの変更によって変更される可能性があり、変更されない可能性があります.
finallyが戻り値を変更していない場合
public static int test55() {
int b = 20;
try {
System.out.println("try block");
return b += 80;
} catch (Exception e) {
System.out.println("catch block");
} finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
}
b = 150;
System.out.println("finally, b =" + b);
}
return 2000;
}
ここの結果は100でfinallyは受けませんでした コードブロックの影響.finallyを実行していないコードではなく、ポイントを中断して一歩一歩実行することができます.return b+=80の行コードの実行が開始されたことが分かった.このときb=100、finallyでb 確かに150です.しかし、System.out.println(「finally,b=」+b)を実行した後、return b+=80の行コードにジャンプしたため、結果は100になった.return b+=80に相当して2回実行した.ここからも説明しますが、finallyでは コードブロックの前にreturnがあります そしてfinally 実行する場合
ステップ1:returnは1回実行し、ローカル変数テーブル(Local Variable Table)に戻り値を保持します.
ステップ2:finallyコードブロックの内容を実行します.
ステップ3:finallyコードブロックを実行した後、再び最終的なreturnに来て、そのメソッドの呼び出し者(invoker)に返します.
finally 戻り値を変更
public static Map getMap() {
Map map = new HashMap();
map.put("KEY", "INIT");
try {
map.put("KEY", "TRY");
return map;
}
catch (Exception e) {
map.put("KEY", "CATCH");
}
finally {
map.put("KEY", "FINALLY");
map = null;
}
return map;
}
ここでの戻り結果はFINALLYです.finallyが実行されました コードブロック、mapも確かにnullです.どうしてですか.やはり上記の3つのステップに従って分析します.最初のステップでreturn mapを初めて実行した場合、map タブで行います. valueはTRYです.JVMでは、値mapが返されます ローカル変数テーブルに保持されます.
ステップ2ではfinallyコードブロックを実行し、まず変更します. map キーをKEYにする 値はTRY キーをKEYに変更 値はFINALLY、map の参照をnullに設定します.説明するのは、実際に作成されたMapオブジェクトは、finallyではなくメモリに存在します(スタック内にあります). 最後にmapを の参照はnullに設定されていますが、スタック内のMapオブジェクトの内容には影響しません.このときスタック内のMapオブジェクトのvalue やはりFINALLYです.
ステップ3 finally コードブロックの実行が終了してからreturn mapを実行すると、以前に保存していたmapが取り出されるので、ここのmapはnullではなく、スタック内のMapオブジェクトの内容を指しています.やはり注意しなければならないのは、ここではMapオブジェクトを返すことです.Stringタイプの変数を返すのではなく、少し特殊です.Stringタイプの変数を返すのではなく、StringだけならTRYを返すべきです.ここではKEYに基づいています. をクリックして対応する値を取得し、スタック内のMapオブジェクトでキーに基づいてKEY 得られた値はFINALLYです.
異常がある場合
public static int test44() {
int b = 20;
try {
System.out.println("try block");
b = b / 0;
return b += 80;
} catch (Exception e) {
b += 15;
System.out.println("catch block");
} finally {
System.out.println("finally block");
if (b > 25) {
System.out.println("b>25, b = " + b);
}
b += 50;
}
return 204;
}
ここで戻りは201ですが、これはよく理解できます. return b+=80の前の行b=b/0が間違っていると、return b+=80の行は実行されず、catchコードブロックに直接入って実行され、finallyに入ります. ブロック実行、finally のb=85、finally 実行が完了すると、最後の行まで実行されて204に戻る.最後の行が戻るbであれば、このとき85である.ここでよく分析してよく理解するのは、returnが最後に、順序的に自然であるためで、上の2つの例のreturn 前にいて、結局飛び上がった.まとめ
もしtry 文は実行されませんでした(tryなど). 前に異常を投げ出したり戻したり)、tryで システム.out(0)が呼び出された場合、JVMが終了しました. は実行されません.
finally文ブロックは、tryまたはcatchまたはfinallyまたは他の場所(最後の行にreturnがあるなど)のreturn文の前に実行されます.次に、catchがある場合とない場合について説明します. 1,catchがある場合、まず異常がある場合にcatchを実行する. a、catchで異常を投げ出すとfinallyが先に実行されます. b,catchにreturnがあると最終結果はcatchにおけるreturnとなり, c,ただし同時にfinallyにreturnがある場合は,異常の有無にかかわらず最終結果はfinallyにおけるreturnである.異常がなければ実行されません catch. 2,catchがない場合,tryとfinallyのみである.異常があるかどうかにかかわらずcatchは自然に実行されません.catchコードがないからです. a,異常がある場合、 tryコードの前に異常が発生した場合(キャプチャされていない)、異常箇所の後は実行されず、finallyも実行されません (すなわち、finallyに対応するtry文ブロックが実行された場合にのみ、finally文ブロックが実行される). 異常がtryに現れるとfinallyが実行されます. b,異常がなければ、 finallyにreturnがある場合、最終結果はfinallyのreturnである. finallyにreturnがない場合、他のreturnを最終結果とします.
参考内容
1,Javaにおけるfinally文ブロックの試行判別についてhttps://www.ibm.com/developerworks/cn/java/j-lo-finally/index.html
2 Java finally文はreturnの前に実行されるのか、それとも後に実行されるのか.https://zhuanlan.zhihu.com/p/85377954