finally 블록은 항상 Java로 실행됩니까?




return try-catch-finally (24)

다음은 Java 언어 사양의 공식 단어입니다.

14.20.2. try-finally 및 try-catch-finally 실행

finally 블록이있는 try 문은 try 블록을 먼저 실행하여 실행됩니다. 그런 다음 선택이 있습니다.

  • try 블록의 실행이 정상적으로 완료되면 [...]
  • try 블록의 실행이 값 Vthrow 때문에 갑자기 완료되면 [...]
  • try 블록의 실행이 다른 이유로 R 에서 갑자기 완료되면 finally 블록이 실행됩니다. 그런 다음 선택이 있습니다.
    • finally 블록이 정상적으로 완료되면 try 문은 이유 R 으로 인해 갑자기 완료됩니다.
    • finally 블록이 이유 S 로 인해 갑자기 완료되면 try 문은 이유 S ( 이유 R 이 삭제됨 )에 따라 갑자기 완료 됩니다 .

return 대한 명세는 실제로 이것을 명백하게 만듭니다 :

JLS 14.17 신고서

ReturnStatement:
     return Expression(opt) ;

Expression 이없는 return 문은 컨트롤을 포함하는 메서드 또는 생성자의 호출자에게 제어를 전달 하려고 시도 합니다.

Expression 있는 return 문은 컨트롤을 포함하는 메소드의 호출자에게 제어를 전달 하려고 시도 합니다. Expression 의 값은 메소드 호출의 값이됩니다.

앞의 설명에서는 try 블록에 return 문이 포함 된 메서드 또는 생성자 내에 try 문이있는 경우 try 문에서 finally 절이 실행되므로 " 제어를 이전 하려고 시도 합니다 "라고되어 있습니다. 순서, 가장 안쪽에서 가장 바깥 쪽, 컨트롤이 메서드 나 생성자의 호출자에게 전달되기 전에. finally 절이 갑자기 완료되면 return 문에서 시작된 제어 전송이 중단 될 수 있습니다.

이 코드를 고려할 때, something() 이 무엇이든 finally 블록이 항상 실행된다는 것을 절대 확신 할 수 있습니까?

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("i don't know if this will get printed out.");
}

대답은 간단합니다.

입력:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e){
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

산출:

catch
finally

결승전은 당신이 가진 어떤 경우에도 항상 호출되기 때문에. 당신은 예외가 없으며, 여전히 호출되어 예외를 잡아냅니다. 여전히 호출됩니다.


finally 블록은 System.exit() 을 호출하거나 스레드가 충돌하지 않는 한 항상 호출되기 때문에 항상 호출됩니다.


이것은 i의 값을 12로 지정했지만 i의 값을 함수에 반환하지 않았기 때문입니다. 올바른 코드는 다음과 같습니다.

public static int test() {
    int i = 0;
    try {
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
        return i;
    }
}

이것을 생각해 볼 수있는 논리적 인 방법은 다음과 같습니다.

  1. finally 블록에 배치 된 코드는 try 블록 내에서 발생 하는 모든 작업을 실행해야합니다.
  2. 따라서 try 블록의 코드가 값을 반환하거나 예외를 throw하려고하면 finally 블록이 실행할 수있을 때까지 항목이 '선반 위에 놓입니다.
  3. finally 블록의 코드는 (정의상) 우선 순위가 높기 때문에 원하는대로 반환하거나 던질 수 있습니다. 이 경우 '선반에 놓아 둔'것은 버려집니다.
  4. 이것에 대한 유일한 예외는 try 블록에서 VM이 완전히 종료되는 경우입니다 (예 : 'System.exit'

마지막으로 항상 실행됩니다. 모든 요소가 반환 된 후에 코드에 나타나기 때문에 구현 방법을 나타내는 것은 아닙니다. Java 런타임은 try 블록을 종료 할 때이 코드를 실행할 책임이 있습니다.

예를 들어 다음과 같은 경우 :

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

런타임은 다음과 같은 것을 생성합니다 :

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

포착되지 않은 예외가 throw되면 finally 블록이 실행되고 예외가 전파됩니다.


다른 응답들에 덧붙여, 'finally'는 try..catch 블록에 의해 어떤 예외 / 반환 값을 오버라이드 할 권한이 있음을 지적하는 것이 중요합니다. 예를 들어, 다음 코드는 12를 반환합니다.

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

마찬가지로 다음 메소드는 예외를 throw하지 않습니다.

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

다음 메소드가 던져 버리는 동안 :

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}

그것은 실제로 어떤 언어에서나 사실입니다 ... 마침내 return 문 앞에 항상 실행됩니다. 리턴은 메소드 본문에 상관없이 항상 실행됩니다. 그것이 사실이 아니라면, finally 블록은별로 의미가 없을 것입니다.



정상적인 실행 과정에서 이것을 고려하십시오 (즉, 예외가 발생하지 않는 경우) : 메소드가 'void'가 아닌 경우 항상 명시 적으로 무언가를 반환하지만 결국에는 항상 실행됩니다.


간략하게, 공식 자바 문서 ( here 클릭 here )에는 다음과 같이 쓰여 있습니다 -

try 또는 catch 코드가 실행되는 동안 JVM이 종료되면 finally 블록이 실행되지 않을 수 있습니다. 마찬가지로, try 또는 catch 코드를 실행하는 스레드가 인터럽트되거나 죽게되면 응용 프로그램 전체가 계속 실행 되더라도 finally 블록이 실행되지 않을 수 있습니다.


예, 가능합니다. JVM 종료 또는 충돌이 발생하지 않는 경우에만


예외가 발생하면 마지막으로 실행됩니다. 예외가 throw되지 않으면 마지막으로 실행됩니다. 예외가 잡히면 마침내 실행됩니다. 예외가 잡히지 않으면 마지막으로 실행됩니다.

JVM이 종료 될 때까지만 실행되지 않습니다.


Kevin의 답을 자세히 설명합니다. 리턴 된 표현식은 나중에 리턴 된 경우에도 finally 평가된다는 것을 아는 것이 중요합니다.

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int printX() {
    System.out.println("X");
    return 0;
}

public static int test() {
    try {
        return printX();
    }
    finally {
        System.out.println("finally trumps return... sort of");
    }
}

산출:

X
finally trumps return... sort of
0

마지막으로 System.exit (0) 호출과 같은 비정상적인 프로그램 종료가 발생하지 않는 한 항상 실행됩니다. 따라서, sysout이 인쇄됩니다.


아니요, 예외적 인 경우는 하나도 없습니다 // System.exit (0); finally 블록이 최종적으로 실행되는 것을 막기 전에.

  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}

예, 그렇게 될 것입니다. try 또는 catch 블록에서 System.exit ()이 호출되거나 JVM이 충돌하지 않는 한 아무 일도 일어나지 않습니다. 블록에 return 문이 있으면 해당 return 문보다 먼저 finally 문이 실행됩니다.


나는 약간의 변경으로 위의 예제를 시도했다.

public static void main(final String[] args) {
    System.out.println(test());
}

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

위 코드는 다음을 출력합니다.

마침내 돌아옵니다.
2

이것은 return i; 이 실행 된 후 finally 블록이 실행되어 12가 i 할당되고 System.out 이 실행됩니다.

finally 블록을 실행 한 후 try 블록은 12를 반환하는 대신 2를 반환합니다.이 반환 문은 다시 실행되지 않기 때문입니다.

이 코드를 이클립스에서 디버깅한다면 System.out 을 실행 한 후 try 블록의 return 문이 try 실행된다는 느낌을 갖게 될 것이다. 그러나 이것은 사실이 아닙니다. 단순히 값 2를 반환합니다.


또한 나쁜 실행이지만 finally 블록 내에 return 문이 있으면 일반 블록의 다른 모든 반환보다 우선합니다. 즉, 다음 블록은 false를 반환합니다.

try { return true; } finally { return false; }

finally 블록에서 예외를 던지는 것과 같은 일입니다.


그것이 결국 블록의 전체 아이디어입니다. 물론, 다른 것들 중에서 돌아 오기 때문에 건너 뛸 수있는 정리 작업을 수행 할 수 있습니다.

try 블록에서 무슨 일이 일어나 든 관계없이 호출됩니다 System.exit(int) 다른 이유로 System.exit(int) 또는 Java Virtual Machine을 호출 하지 않는 한 ).


예제 코드 :

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

산출:

finally trumps return. 
0

마지막으로 블록은 항상 예외 처리 여부를 실행합니다. try 블록 전에 예외가 발생하면 finally 블록이 실행되지 않습니다.


이것을 생각해 볼 수있는 논리적 인 방법은 다음과 같습니다.

finally 블록에 배치 된 코드는 try 블록 내에서 발생하는 모든 작업을 실행해야합니다.

따라서 try 블록의 코드가 값을 반환하거나 예외를 throw하려고하면 항목이 finally 블록이 실행될 때까지 '선반에 놓이기'finally 블록의 코드는 (정의상) 우선 순위가 높기 때문에 반환하거나 throw 할 수 있습니다 좋아하는 것은 무엇이든간에. 이 경우 '선반에 놓아 둔'것은 버려집니다.

이것에 대한 유일한 예외는 try 블록에서 VM이 완전히 종료되는 경우입니다 (예 : 'System.exit'

finally 블록에서 예외를 던지지 마십시오.

try {
  someMethod();  //Throws exceptionOne
} finally {
  cleanUp();    //If finally also threw any exception the exceptionOne will be lost forever
}

cleanUp ()이 예외를 던지지 않는 한 괜찮습니다. 위의 예에서 someMethod ()가 예외를 throw하고 finally 블록에서도 cleanUp ()이 예외를 throw하면 두 번째 예외가 메서드에서 벗어나 원래의 첫 번째 예외 (올바른 이유)가 영원히 손실됩니다. finally 블록에서 호출 한 코드가 예외를 throw 할 수있는 경우 처리하거나 로그해야합니다. 드디어 최종 블록에서 나오게하지 마라.

실제로 System.exit ()을 호출하거나 프로세스를 중단시키는 치명적인 오류 (Windows에서 "핫 스폿"또는 "Dr Watson"으로 비공식적으로 참조되는)을 발생시켜 프로그램을 종료하면 finally 블록이 표시되지 않습니다. 실행!

try / catch / finally 블록을 중첩하는 것을 막을 수있는 방법은 없습니다 (예 : try / catch 블록 안에 try / finally 블록을 넣거나 그 반대의 경우). 그렇게하는 것은 흔한 일이 아닙니다.





try-catch-finally