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

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

Aug 23, 2017 am 10:19 AM
finally java return

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

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Javaの平方根 Javaの平方根 Aug 30, 2024 pm 04:26 PM

Java の平方根のガイド。ここでは、Java で平方根がどのように機能するかを、例とそのコード実装をそれぞれ示して説明します。

Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

Java の乱数ジェネレーター Java の乱数ジェネレーター Aug 30, 2024 pm 04:27 PM

Java の乱数ジェネレーターのガイド。ここでは、Java の関数について例を挙げて説明し、2 つの異なるジェネレーターについて例を挙げて説明します。

Javaのアームストロング数 Javaのアームストロング数 Aug 30, 2024 pm 04:26 PM

Java のアームストロング番号に関するガイド。ここでは、Java でのアームストロング数の概要とコードの一部について説明します。

ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

See all articles