Java 예외가 발생하지 않습니까?


170

try-catch 구성에 작은 이론적 문제가 있습니다.

어제 Java에 대한 실기 시험을 보았는데 다음 예제를 이해하지 못합니다.

try {
    try {
        System.out.print("A");
        throw new Exception("1");
    } catch (Exception e) {
        System.out.print("B");
        throw new Exception("2");
    } finally {
        System.out.print("C");
        throw new Exception("3");
    }
} catch (Exception e) {
    System.out.print(e.getMessage());
}

문제는 "출력 결과는 어떻습니까?"

나는 그것이 AB2C3 일 것이라고 확신했지만, 놀랍습니다. 그렇지 않습니다.

정답은 ABC3입니다. (실제 테스트와 동일합니다.)

제 질문은 Exception ( "2")이 어디로 갔습니까?


8
+1 아아, 나는이 대답을 알고 있었다. 나는 인터뷰에서 이것을 물었다. try / catch / finally가 스택에서 어떻게 작동하는지 이해하는 것은 매우 좋은 질문입니다.
그러나 나는 래퍼 클래스가 아닙니다.

10
숫자를 인쇄 할 수있는 print 문은 하나뿐입니다 (마지막 :) print(e.getMessage()). 출력이 될 것이라고 AB2C3생각했습니다. 가장 바깥 쪽 catch블록이 두 번 실행될 것이라고 생각 했 습니까?
Adrian Pronk

Java에서는 catch 블록 밖으로 제어를 전송하는 명령이 실행되기 전에 finally 블록이 있으면 실행됩니다. finally 블록의 코드 만 제어를 외부로 전송하지 않으면 catch 블록의 지연된 명령이 실행됩니다.
토마스

답변:


198

로부터 Java 언어 사양 14.20.2. :

이유 R로 인해 catch 블록이 갑자기 완료되면 finally 블록이 실행됩니다. 그런 다음 선택이 있습니다.

  • finally 블록이 정상적으로 완료되면 이유 R로 인해 try 문이 갑자기 완료됩니다.

  • finally 블록이 이유 S에 대해 갑자기 완료되면 try 문은 이유 S에 대해 갑자기 완료됩니다 (그리고 이유 R은 폐기 됨) .

따라서 예외를 발생시키는 catch 블록이있는 경우 :

try {
    // ...
} catch (Exception e) {
    throw new Exception("2");
}

그러나 finally 블록도 있으며 예외도 발생합니다.

} finally {
    throw new Exception("3");
}

Exception("2")버리고 Exception("3")전파됩니다.


72
이것은 심지어 return진술 에도 적용됩니다 . finally 블록에 수익이 있으면 a try또는 catch블록의 모든 수익보다 우선합니다 . 이러한 "기능"으로 인해 블록이 예외를 throw하거나 return 문을 가져서는 안된다는 것이 좋습니다 .
Augusto

또한 리소스 사용시 try-with-resources가 Java 7의 장점을 상속받습니다. 리소스를 닫을 때 보조 예외가 생성되어 초기 디버깅을 더 쉽게 수행 할 수 있도록 초기 예외를 유지합니다.
w25r

19

finally 블록에서 발생한 예외는 try 또는 catch 블록에서 이전에 발생한 예외를 억제합니다.

Java 7 예 : http://ideone.com/0YdeZo

에서 자바 독의 예 :


static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

그러나이 예제에서 readLine 메소드와 두 예외 모두를 처리하는 경우 readFirstLineFromFileWithFinallyBlock 메소드는 finally 블록에서 발생한 예외를 처리합니다. try 블록에서 발생한 예외는 억제됩니다.


try-withJava 7 의 새로운 구문은 예외 억제의 또 다른 단계를 추가합니다. try 블록에서 발생한 예외는 try-with 부분에서 이전에 발생한 예외를 억제합니다.

같은 예에서 :

try (
        java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
        java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
    ) {
        for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {
            String newLine = System.getProperty("line.separator");
            String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;
            writer.write(zipEntryName, 0, zipEntryName.length());
        }
    }

try-with-resources 문과 관련된 코드 블록에서 예외가 발생할 수 있습니다. 위의 예에서, try 블록에서 예외가 발생 될 수 있으며 ZipFile 및 BufferedWriter 객체를 닫으려고 할 때 try-with-resources 문에서 최대 2 개의 예외가 발생 될 수 있습니다. try 블록에서 예외가 발생하고 try-with-resources 문에서 하나 이상의 예외가 발생하면 try-with-resources 문에서 발생 된 예외가 억제되고 블록에서 발생한 예외는 예외입니다. 이것은 writeToFileZipFileContents 메소드에 의해 발생합니다. try 블록에서 발생한 예외에서 Throwable.getSuppressed 메소드를 호출하여 이러한 억제 된 예외를 검색 할 수 있습니다.


문제의 코드에서 각 블록은 오래된 예외를 무시하고 로깅조차하지 않으며 버그를 해결하려고 할 때 좋지 않습니다.

http://en.wikipedia.org/wiki/Error_hiding


9

throw new Exception("2");catch아닌 블록 에서 발생 하므로 try다시 잡히지 않습니다. 14.20.2를
참조하십시오 . 의 실행 마지막으로-하려고 시도 - 캐치 - 드디어 .

이것이 일어나는 일입니다.

try {
    try {
        System.out.print("A");         //Prints A
        throw new Exception("1");   
    } catch (Exception e) { 
        System.out.print("B");         //Caught from inner try, prints B
        throw new Exception("2");   
    } finally {
        System.out.print("C");         //Prints C (finally is always executed)
        throw new Exception("3");  
    }
} catch (Exception e) {
    System.out.print(e.getMessage());  //Prints 3 since see (very detailed) link
}

네 맞습니다. 이런 일이 일어나고 있지만 설명을 찾고있었습니다. 왜 이런 식으로 행동하는지
Kousalik

5

귀하의 질문은 매우 분명하고 대답은 같은 정도로 간단합니다. 메시지가 "2"인 Exception 객체는 메시지가 "3"인 Exception 객체로 덮어 씁니다.

설명 : 예외가 발생하면 처리 할 블록을 잡기 위해 발생한 객체입니다. 그러나 catch 블록 자체에서 예외가 발생하면 예외 처리를 위해 해당 개체가 OUTER CATCH Block (있는 경우)으로 전송됩니다. 그리고 여기에서도 마찬가지입니다. 메시지가 "2"인 예외 오브젝트가 OUTER catch 블록으로 전송됩니다. 그러나 기다립니다 . 내부 try-catch 블록을 떠나기 전에 마지막으로 실행해야합니다. 여기에 우리가 걱정하는 변화가 일어났습니다. 새로운 EXCEPTION 객체 (메시지 "3")가 발생하거나 이미 발생 된 Exception 객체 (메시지 "2")를 대체 한이 블록이 최종적으로 차단되어 결과적으로 Exception 객체의 메시지가 인쇄 될 때 재정의 된 값, 즉 "2"가 아닌 "3"입니다.

주의 사항 : CATCH 블록에서는 하나의 예외 객체 만 처리 할 수 ​​있습니다.


2

finally블록은 항상 실행됩니다. 어느 쪽이든 당신 returntry 블록 또는 예외 내부에서가 발생합니다. finally블록 에서 발생한 예외 는 catch 분기에서 발생한 예외 를 무시합니다.

또한 예외를 throw해도 자체 출력이 발생하지 않습니다. 줄 throw new Exception("2");은 아무것도 쓰지 않습니다.


1
예, 예외 출력을 단독으로 던지는 것을 알고 있지만 예외 2를 삭제 해야하는 이유를 보지 못했습니다. 나는 조금 더 똑똑해 :-)
Kousalik

항상 아주 오랜 시간이고 아주 오랜 시간에 어떤 일이 발생할 수 있습니다 (퍼즐 wouter.coekaerts.be/2012/puzzle-dreams 확인 )
Dainius

0

귀하의 코드에 따르면 :

try {
    try {
        System.out.print("A");
        throw new Exception("1");   // 1
    } catch (Exception e) {
        System.out.print("B");      // 2
        throw new Exception("2");
    } finally {                     // 3
        System.out.print("C");      // 4 
        throw new Exception("3");
    }
} catch (Exception e) {             // 5
    System.out.print(e.getMessage());
}

여기에서 볼 수 있듯이 :

  1. A를 인쇄하고 예외를 던집니다 # 1.
  2. 이 예외는 catch 문과 print에 의해 포착되었습니다 B - # 2.
  3. block은 # 3try-catch (또는 예외가 발생하지 않은 경우에만 시도) 명령문 이후에 마지막으로 실행 C - # 4되고 새 예외를 인쇄 하고 발생시킵니다.
  4. 이것은 외부 catch 문에 의해 잡혔다 # 5;

결과는 ABC3입니다. 그리고 2같은 방식으로 생략1


죄송합니다, Exception ( "1")은 생략되지 않았지만 성공적으로 발견되었습니다
Black Maggie

@Black Maggie 캐시되어 새로운 예외가 발생했습니다 => 캐시되지 않고 프로그램이 종료되었습니다. 그리고이 블록이 마침내 실행되기 전에.
nazar_art
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.