この記事では主にJavaのtryfinallyreturn文の実行シーケンスの簡単な分析を紹介します
問題分析
finally文ブロックは実行されるでしょうか?
おそらく、多くの人の最初の反応は、間違いなく実装するだろうということですが、よく考えてみると、間違いなく実装するのであれば、そのようなSBには依頼しません。
Demo1
public class Test { public static void main(String[] args) { System.out.println("return value of test(): " + test()); } public static int test() { int i = 1; // if (i == 1) { // return 0; // } System.out.println("the previous statement of try block"); i = i / 0; try { System.out.println("try block"); return i; } finally { System.out.println("finally block"); } } }
Demo1の実行結果は以下の通りです:
the previous statement of try block Exception in thread "main" java.lang.ArithmeticException: / by zero at com.becoda.bkms.bus.basics.web.Test2.test(Test2.java:15) at com.becoda.bkms.bus.basics.web.Test2.main(Test2.java:5)
さらに、上記の例のコメントを削除すると、実行結果は次のようになります:
return value of test(): 0
上記 2 つの状況では、finally ステートメント ブロックが実行されません。何が問題ですか? finally ステートメント ブロックは、finally ステートメント ブロックに対応する try ステートメント ブロックが実行される場合にのみ実行されます。以上のことは、try ステートメント ブロックの前にリターン (return) または例外をスローするため、try ステートメントに対応するfinally ステートメント ブロックは実行されませんでした。では、finallyに対応するtry文ブロックが実行されたとしても、finally文ブロックは必ず実行されるのでしょうか?しかし、次の例
Demo2
public class Test { public static void main(String[] args) { System.out.println("return value of test(): " + test()); } public static int test() { int i = 1; try { System.out.println("try block"); System.exit(0); return i; } finally { System.out.println("finally block"); } } }
Demo2 の実行結果は次のようになります:
try block
finally ステートメント ブロックがまだ実行されません。なぜですか?これは、try ステートメント ブロックで System.exit(0) ステートメントを実行し、Java 仮想マシンの実行を終了したためです (通常の状況ではこれは行われません)。また、try 文ブロックや catch 文ブロックの実行中にスレッドが中断 (割り込み) または終了 (強制終了) されると、対応するfinally 文ブロックが実行されない場合があります。さらに極端な状況もあります。つまり、スレッドが try ステートメント ブロックまたは catch ステートメント ブロックを実行しているときに、突然クラッシュするか電源が失われ、finally ステートメント ブロックは確実に実行されなくなります。
最後にステートメント例の説明
以下の簡単な例を見てください
Demo3
public class Test { public static void main(String[] args) { try { System.out.println("try block"); return; } finally { System.out.println("finally block"); } } }
Demo3の実行結果は次のとおりです:
Demo3 では、finally ステートメントについて説明しています。ブロックは、ステートメント ブロック内の return ステートメントの前に実行される try にあります。別の例を見てみましょう。Demo4
try block finally block
public class Test { public static void main(String[] args) { System.out.println("reture value of test() : " + test()); } public static int test() { int i = 1; try { System.out.println("try block"); i = 1 / 0; return 1; } catch (Exception e) { System.out.println("exception block"); return 2; } finally { System.out.println("finally block"); } } }
上記の Demo3 と Demo4 から、finally ステートメント ブロックは実際には try または catch の return ステートメントの前に実行されることがわかります。より一般的には、finally ステートメント ブロックは return に加えて、コントロール転送ステートメントの前に実行される必要があります。 transfer ステートメントには、break と continue も含まれます。
次の 2 つの例を見てください
Demo5
try block exception block finally block reture value of test() : 2
getValue() の戻り値: 1
Demo6
りー
Demo6 の実行結果は次のとおりです:public class Test { public static void main(String[] args) { System.out.println("return value of getValue(): " + getValue()); } public static int getValue() { try { return 0; } finally { return 1; } } }
この問題を説明するには、Java 仮想マシンがfinally ステートメント ブロックをコンパイルする方法を理解する必要があります。
Java メソッドはスタック フレームで実行されます。スタック フレームは、メソッドを実行するスレッドのプライベート スタックの単位であり、メソッドの実行時にメモリ領域として各メソッドに小さな領域が割り当てられます。
1. 実行中の式のオペランドを保存するために使用されるオペランド スタック。
2. メソッド パラメーター、メソッド内で宣言された変数など、メソッドで使用される変数を保存するために使用されます。メソッド内で使用されるオブジェクトのメンバー変数またはクラスのメンバー変数(静的変数)のうち、最後の 2 つの変数はローカル変数領域にコピーされるため、マルチスレッド環境では、このような変数は次のように宣言する必要があります。必要に応じて volatile 型
3 、バイトコード命令領域
たとえば、次のコード
public class Test { public static void main(String[] args) { System.out.println("return value of getValue(): " + getValue()); } public static int getValue() { int i = 1; try { return i; } finally { i++; } } }
1. 実行: 式を計算し、結果をオペランド スタックの先頭に保存します。
2. 実行: オペランド スタックの先頭の値 (式の結果) をコピーします。戻り値としてローカル変数領域に格納します
3、执行:finally语句块中的代码;
4、执行:将第2步复制到局部变量区的返回值又复制回操作数栈顶;
5、执行:return指令,返回操作数栈顶的值;
我们可以看到,在第一步执行完毕后,整个方法的返回值就已经确定了,由于还要执行finally代码块,因此程序会将返回值暂存在局部变量区,腾出操作数栈用来执行finally语句块中代码,等finally执行完毕,再将暂存的返回值又复制回操作数栈顶。所以无论finally语句块中执行了什么操作,都无法影响返回值,所以试图在finally语句块中修改返回值是徒劳的。因此,finally语句块设计出来的目的只是为了让方法执行一些重要的收尾工作,而不是用来计算返回值的。
这样就能解释Demo6的问题了
让我们再来看以下 3 个例子。
Demo7
public class Test { public static void main(String[] args) { System.out.println("return value of getValue(): " + getValue()); } @SuppressWarnings("finally") public static int getValue() { int i = 1; try { i = 4; } finally { i++; return i; } } }
Demo7的执行结果为:
return value of getValue(): 5
Demo8
public class Test { public static void main(String[] args) { System.out.println("return value of getValue(): " + getValue()); } public static int getValue() { int i = 1; try { i = 4; } finally { i++; } return i; } }
Demo8的执行结果为:
return value of getValue(): 5
Demo9
public class Test { public static void main(String[] args) { System.out.println(test()); } public static String test() { try { System.out.println("try block"); return test1(); } finally { System.out.println("finally block"); } } public static String test1() { System.out.println("return statement"); return "after return"; } }
Demo9的执行结果为:
try block return statement finally block after return
总结:
1、finally 语句块不一定会被执行
2、finally 语句块在 try 语句块中的 return 语句之前执行
3、finally 语句块在 catch 语句块中的 return 语句之前执行
4、finally 语句块中的 return 语句会覆盖 try 块中的 return 返回
5、试图在 finally 语句块中修改返回值不一定会被改变
以上がJava の try、finally、return ステートメントの実行順序の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。