ホームページ > Java > &#&チュートリアル > Java の try、finally、return ステートメントの実行順序

Java の try、finally、return ステートメントの実行順序

黄舟
リリース: 2017-08-23 10:19:43
オリジナル
2428 人が閲覧しました

この記事では主に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
ログイン後にコピー

Demo4 の実行結果は次のとおりです。


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");
    }
  }
}
ログイン後にコピー

Demo4 は、finally ステートメント ブロックが catch ステートメント ブロックの return ステートメントの前に実行されることを示しています。


上記の Demo3 と Demo4 から、finally ステートメント ブロックは実際には try または catch の return ステートメントの前に実行されることがわかります。より一般的には、finally ステートメント ブロックは return に加えて、コントロール転送ステートメントの前に実行される必要があります。 transfer ステートメントには、break と continue も含まれます。

次の 2 つの例を見てください

Demo5

try block
exception block
finally block
reture value of test() : 2
ログイン後にコピー

Demo5 の実行結果は次のとおりです:


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;
    }
  }
}
ログイン後にコピー

上記の分析から得られた結論を使用すると、finally ステートメント ブロックは try または catch の return ステートメントの前に実行されます。 このことから、Demo5 の実行結果が 1 であることが容易にわかります。 finally の return 1; ステートメントは try の return 0; ステートメントが実行される前に実行されるため、プログラムの制御は呼び出し元の main() 関数に転送され、値は次のようになります。 1.では、なぜ Demo6 の戻り値は 2 ではなく 1 なのでしょうか? Demo5 の分析ロジックによれば、finally の i++ ステートメントは try? の return i より前に実行される必要があります。 i の初期値は 1 で、i++ を実行すると 2 になり、return i を実行すると 2 になるはずではありませんか。どうやって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++;
    }
  }
}
ログイン後にコピー

まず、finally ステートメントは必ず実行されることはわかりますが、その実行順序はどうなるのでしょうか?それらの実行順序は次のとおりです:


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 サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート