Heim  >  Artikel  >  Java  >  Die Ausführungsreihenfolge von Try-, Final- und Return-Anweisungen in Java

Die Ausführungsreihenfolge von Try-, Final- und Return-Anweisungen in Java

黄舟
黄舟Original
2017-08-23 10:19:432386Durchsuche

Dieser Artikel stellt hauptsächlich eine kurze Analyse der Ausführungssequenz der try-final-return-Anweisung in Java vor. Freunde in Not können sich auf

Problemanalyse

Wird der „finally“-Anweisungsblock ausgeführt?

Vielleicht ist die erste Reaktion vieler Menschen, dass sie es auf jeden Fall umsetzen werden, aber wenn sie sorgfältig darüber nachdenken, ob sie es auf jeden Fall umsetzen werden, werden sie eine solche SB-Frage nicht stellen.

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");
    }
  }
}
Die Ausführungsergebnisse von Demo1 lauten wie folgt:


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)
Wenn Sie außerdem die Kommentare im obigen Beispiel entfernen, lautet das Ausführungsergebnis:


return value of test(): 0
In den beiden oben genannten Fällen schließlich: Was ist das Problem, wenn der Anweisungsblock nicht ausgeführt wird? Der finale Anweisungsblock wird nur ausgeführt, wenn der try-Anweisungsblock, der dem finalen Anweisungsblock entspricht, ausgeführt wird. Die obigen Anweisungen geben alle vor dem try-Anweisungsblock zurück (return) oder werfen eine Ausnahme aus, sodass der finale Anweisungsblock der try-Anweisung entspricht nicht ausgeführt. Selbst wenn der try-Anweisungsblock, der „final“ entspricht, ausgeführt wird, wird der „final“-Anweisungsblock dann definitiv ausgeführt? Aber im folgenden Beispiel

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");
    }
  }
}
Das Ausführungsergebnis von Demo2 ist wie folgt :


try block
Der final-Anweisungsblock wird immer noch nicht ausgeführt, warum? Weil wir die System.exit(0)-Anweisung im try-Anweisungsblock ausgeführt haben und damit die Ausführung der Java Virtual Machine beendet haben, obwohl wir dies unter normalen Umständen nicht tun würden. Es gibt auch eine Situation, in der, wenn ein Thread während der Ausführung eines Try-Anweisungsblocks oder Catch-Anweisungsblocks unterbrochen (unterbrochen) oder beendet (getötet) wird, der entsprechende Final-Anweisungsblock möglicherweise nicht ausgeführt wird. Es gibt auch eine extremere Situation: Wenn der Thread einen Try-Anweisungsblock oder einen Catch-Anweisungsblock ausführt, stürzt er plötzlich ab oder verliert die Leistung, und der Final-Anweisungsblock wird definitiv nicht ausgeführt.

Schließlich Beispiel für eine Anweisung

Schauen wir uns ein einfaches Beispiel an

Demo3


public class Test {
  public static void main(String[] args) {
    try {
      System.out.println("try block");
      return;
    } finally {
      System.out.println("finally block");
    }
  }
}
Das Ausführungsergebnis von Demo3 ist:


try block
finally block
Demo3-Beschreibung schließlich Anweisungsblock Wird vor der return-Anweisung im try-Block ausgeführt. Schauen wir uns ein anderes Beispiel an.

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");
    }
  }
}
Das Ausführungsergebnis von Demo4 ist:


try block
exception block
finally block
reture value of test() : 2
Demo4 zeigt, dass der „finally“-Anweisungsblock vor der „return“-Anweisung im „catch“-Anweisungsblock ausgeführt wird.

Aus Demo3 und Demo4 können wir ersehen, dass der finale Anweisungsblock tatsächlich vor der return-Anweisung in try oder Catch ausgeführt wird. Im Allgemeinen sollte der finale Anweisungsblock vor der Kontrollübertragungsanweisung ausgeführt werden Zusätzlich zu Return enthält die Kontrollübertragungsanweisung auch Break und Continue.

Schauen wir uns die folgenden zwei Beispiele an

Demo5


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;
    }
  }
}
Demo5 Das Ausführungsergebnis ist:

Rückgabewert von getValue(): 1

Demo6


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++;
    }
  }
}
Das Ausführungsergebnis von Demo6 ist:


return value of getValue(): 1
Unter Verwendung der Schlussfolgerung aus unserer obigen Analyse: Der final-Anweisungsblock ist die Rückgabe in try oder Catch wird vor der Anweisung ausgeführt. Daraus können wir leicht verstehen, dass das Ausführungsergebnis von Demo5 1 ist. Da die Anweisung „return 1;“ in „final“ vor der Anweisung „return 1; 1. Warum ist der Rückgabewert von Demo6 also nicht 2, sondern 1? Gemäß der Analyselogik von Demo5 sollte die i++;-Anweisung in „finally“ ausgeführt werden, bevor „return i“ in try? Der Anfangswert von i ist 1, dann ist er 2, und dann ist er 2, und dann soll er 2 sein. Wie ist daraus 1 geworden?

Um dieses Problem zu erklären, müssen Sie verstehen, wie die Java Virtual Machine den final-Anweisungsblock kompiliert.

Java-Methoden werden in Stapelrahmen ausgeführt. Der Thread, der die Methode ausführt, reserviert für jede Methode einen kleinen Speicherplatz Stapelrahmen Unterteilt in drei Bereiche:

1. Operandenstapel, der zum Speichern der Operanden im ausgeführten Ausdruck verwendet wird

2. Lokaler Variablenbereich, der zum Speichern der in Methoden verwendeten Variablen verwendet wird, einschließlich Methode Parameter, innerhalb der Methode deklarierte Variablen und Mitgliedsvariablen des in der Methode verwendeten Objekts oder Klassenmitgliedsvariablen (statische Variablen). Die letzten beiden Variablen werden in den lokalen Variablenbereich kopiert, also in einer Multithread-Umgebung müssen nach Bedarf als flüchtige Typen deklariert werden

3. Bytecode-Anweisungsbereich

Zum Beispiel der folgende Code


try{
  return expression;
}finally{
  do some work;
}
Zunächst einmal wissen wir, dass „finally“-Anweisungen definitiv ausgeführt werden, aber wie ist ihre Ausführungsreihenfolge? Ihre Ausführungsreihenfolge ist wie folgt:

1. Ausführung: Ausdruck, berechnen Sie den Ausdruck und das Ergebnis wird oben im Operandenstapel gespeichert.

2 Der Operandenstapel (das Ergebnis von Ausdruck) wird als Rückgabewert in den lokalen Variablenbereich kopiert

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 语句块中修改返回值不一定会被改变

Das obige ist der detaillierte Inhalt vonDie Ausführungsreihenfolge von Try-, Final- und Return-Anweisungen in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn