이 글은 주로 Java의 try finally return 문의 실행 순서에 대한 간략한 분석을 소개합니다. 필요하신 분들은 참고하시면 됩니다.
문제 분석
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
위의 두 가지 상황에서 finally 문 블록이 실행되지 않는데 무엇이 문제인가요? finally 문 블록은 finally 문 블록에 해당하는 try 문 블록이 실행될 때만 실행됩니다. 그러나 위의 모든 반환(return) 또는 try 문 블록 이전에 예외를 발생시키므로 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 문 블록이 여전히 실행되지 않는 이유는 무엇입니까? 일반적으로 이 작업을 수행하지는 않지만 Java 가상 머신의 실행을 종료하는 try 문 블록에서 System.exit(0) 문을 실행했기 때문입니다. try 문 블록이나 catch 문 블록을 실행하는 도중 스레드가 중단(interrupted)되거나 종료(kill)되는 경우 해당 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 문 이전에 실행 시도 중입니다. 또 다른 예를 살펴보겠습니다.Demo4
try block finally blockDemo4의 실행 결과는 다음과 같습니다.
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를 보면 실제로 try 또는 catch에서 return 문 이전에 finally 문 블록이 실행되는 것을 볼 수 있습니다. 보다 일반적으로 finally 문 블록은 제어 전달 문 이전에 실행되어야 합니다. transfer 문에는 break와 continue도 포함됩니다.
다음 두 가지 예를 보세요
Demo5
try block exception block finally block reture value of test() : 2Demo5의 실행 결과는 다음과 같습니다.
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; } } }위 분석에서 도출한 결론을 사용하면 try 또는 catch에서 return 문 이전에 finally 문 블록이 실행됩니다. 이를 통해 Demo5의 실행 결과가 1이라는 것을 쉽게 이해할 수 있습니다. finally의 return 1; 문은 try의 return 0; 문보다 먼저 실행되므로 finally의 return 1; 문이 실행된 후에는 프로그램의 제어가 호출자 main() 함수로 전달되고 값은 다음과 같습니다. 1. 그러면 Demo6의 반환 값이 2가 아닌 1인 이유는 무엇입니까? Demo5의 분석 로직에 따르면, try?에서 return i 전에 finally의 i++; 문이 실행되어야 합니다. i의 초기 값은 1이고, i++를 실행하면 2가 되며, return i를 실행하면 2가 되어야 하지 않나요? 어떻게 1이 되었나요?
이 문제를 설명하려면 Java 가상 머신이 finally 문 블록을 컴파일하는 방법을 이해해야 합니다.
Java 메소드는 스택 프레임에서 실행됩니다. 스택 프레임은 메소드를 실행하는 스레드가 메소드 실행 시 메모리 공간으로 각 메소드에 작은 공간을 할당합니다. 세 가지 영역으로 구분:
1. 실행 중인 표현식의 피연산자를 저장하는 데 사용되는 피연산자 스택
2. 메소드 매개변수, 메소드 내부에 선언된 변수를 포함하여 메소드에 사용되는 변수를 저장하는 데 사용되는 지역 변수 영역 메소드에 사용된 객체의 멤버 변수나 클래스의 멤버 변수(정적 변수) 중 마지막 두 변수는 로컬 변수 영역에 복사되므로 멀티 스레드 환경에서는 이러한 변수를 다음과 같이 선언해야 합니다. 필요에 따라 휘발성 유형
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!