캐치 블록 내부에 예외가 발생했습니다. 다시 포착됩니까?


180

이것은 프로그래밍 101 질문처럼 보일 수 있으며 대답을 알고 있지만 다시 확인해야한다고 생각했습니다. 아래의이 코드에서, 첫 번째 catch 블록에서 발생한 예외가 아래의 일반 Exception catch 블록에 의해 포착됩니까?

try {
  // Do something
} catch(IOException e) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

나는 항상 대답이 '아니오'라고 생각했지만 지금은 이것으로 인해 이상한 행동을 할 수 있습니다. 대답은 아마도 대부분의 언어에서 동일하지만 Java에서 일하고 있습니다.


2
아마도 "이상한 행동"을 설명 할 수 있습니까?
Jeffrey L Whitledge

ApplicationException이 다른 곳에서 발생하지 않고이 블록으로 전파되지 않습니까?
sblundy

Eclipse IDE에서 이것을 알았습니다. "새 예외 발생"을 try 블록에 넣도록 강요하고 있지만 이유를 모르겠습니다. 나는 그것을하지 않고 과거에 그것을했습니다. try 블록이 왜 필요한지 모르겠습니다. Google의 많은 예제는 try 블록이 필요없는 사람들을 보여줍니다. if 문 안에 던지기 때문입니까?
djangofan

답변:


214

새로운 throw것은 try직접 블록에 없기 때문에 아닙니다 .


코드가 이와 같다면, {{catch (Exception e) {System.err.println ( "Catch Exception :"+ e.getClass ()); } catch (IOException e) {System.err.println ( "IOException 잡기 :"+ e.getClass ()); } try 블록의 코드는 IO Exception을 생성합니다. 즉각 일반 Exception 블록으로 이동합니까 아니면 IOException catch 블록으로 이동합니까?
sofs1

4
@ user3705478 이런 종류의 잘못된 상황을 피하기 위해 코드가 컴파일되지 않습니다. 일반적으로 동일한 try 블록에서 수퍼 클래스를 잡은 후 서브 클래스를 잡을 수 없습니다.
Chris Jester-Young

감사. 컴파일 시간 오류가 발생합니까? 집에 도착하면 테스트 해 보겠습니다.
sofs1

@ user3705478에 따라 다르지만 블록 RuntimeException에서 a 를 던지면 catch컴파일 오류가 없습니다.
랜디 데브

@AndrewDunn 나는 그것이 user3705478의 질문에 관한 것이 아니라 오히려 부모 예외 catch절이 자식 예외 절 앞에 나열 되면 어떻게되는지 생각합니다 catch. 내 이해는 Java가 이것을 허용하지 않으며 컴파일 타임에 잡힌다는 것입니다.
Chris Jester-Young

71

아니요. 확인하기가 매우 쉽습니다.

public class Catch {
    public static void main(String[] args) {
        try {
            throw new java.io.IOException();
        } catch (java.io.IOException exc) {
            System.err.println("In catch IOException: "+exc.getClass());
            throw new RuntimeException();
        } catch (Exception exc) {
            System.err.println("In catch Exception: "+exc.getClass());
        } finally {
            System.err.println("In finally");
        }
    }
}

인쇄해야합니다 :

catch IOException에서 : 클래스 java.io.IOException
마침내
스레드 "main"의 예외 java.lang.RuntimeException
        Catch.main (Catch.java:8)에서

기술적으로는 컴파일러 버그, 구현 의존적, 지정되지 않은 동작 등일 수 있습니다. 그러나 JLS는 잘 정리되어 있으며 컴파일러는 이런 종류의 간단한 일에 충분합니다 (일반 코너 케이스는 다를 수 있습니다).

또한 두 catch 블록을 서로 바꾸면 컴파일되지 않습니다. 두 번째 캐치는 완전히 도달 할 수 없습니다.

catch 블록이 실행 되더라도 finally 블록은 항상 실행됩니다 (무한 루프, 도구 인터페이스를 통한 연결 및 스레드 종료, 바이트 코드 다시 작성 등).


3
finally물론 피하는 가장 확실한 방법 은 전화하는 것 System.exit입니다. :-P
Chris Jester-Young

3
@Chris Jester-Young for(;;);는 더 짧고, 언어에 포함되어 있으며 부작용의 방식을 많이 소개하지 않으며 나에게 더 분명합니다.
Tom Hawtin-tackline

1
System.exitCPU에 친숙합니다! : -O 그러나 네, 물론 이것은 주관적인 기준입니다. 또한, 나는 당신이 코드 골퍼가 될 줄 몰랐습니다. ;-)
Chris Jester-Young

28

Java 언어 사양은 섹션 14.19.1에 나와 있습니다.

값 V가 발생하여 try 블록의 실행이 갑자기 완료되면 다음을 선택할 수 있습니다.

  • V의 런타임 유형이 try 문의 모든 catch 절의 매개 변수에 지정 가능한 경우, 첫 번째 (가장 왼쪽) catch 절이 선택됩니다. 선택한 catch 절의 매개 변수에 V 값이 할당되고 해당 catch 절의 블록이 실행됩니다. 해당 블록이 정상적으로 완료되면 try 문이 정상적으로 완료됩니다. 어떤 이유로 든 해당 블록이 갑자기 완료되면 try 문도 같은 이유로 갑자기 완료됩니다.

참조 : http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

다시 말해, 예외를 처리 할 수있는 첫 번째 엔 클로징 캐치가 예외를 처리하는 경우 원래 시도에 대한 다른 캐치 범위에 속하지 않으므로 처리하려고 시도하지 않습니다.

한 가지 관련되고 혼란스러운 점은 try- [catch] -finally 구조에서 finally 블록에서 예외가 발생할 수 있으며, 그렇다면 try 또는 catch 블록에서 발생한 예외가 손실된다는 것입니다. 처음 볼 때 혼란 스러울 수 있습니다.


Java 7부터는 try-with-resources 를 사용하지 않도록 추가하고 싶었습니다 . 그런 다음 tryAND가 finally둘 다 발생하면 finally억제되지만 from의 예외에 추가됩니다 try. 경우 catch뿐만 아니라 발생하면 'addSuppressed'를 통해 그 자신을 처리하고 추가하지 않는 한, 당신은 행운에있어 try예외를 - 다음 세 가지가 있습니다.
LAFK는 모니카의 복직 자 모니카

6

catch 블록에서 예외를 발생 시키려면 메소드 / 클래스 등에 게 알려야합니다. 그 예외를 던져야합니다. 이렇게 :

public void doStuff() throws MyException {
    try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

그리고 지금 컴파일러는 당신에게 소리 지르지 않을 것입니다 :)


4

아니요-Chris Jester-Young이 말했듯이 계층 구조의 다음 try-catch까지 발생합니다.


2

위에서 말했듯이 ...
나는 무슨 일이 일어나고 있는지 보는 데 어려움이 있다면 디버거에서 문제를 재현 할 수 없다면 새로운 예외를 다시 던지기 전에 추적을 추가 할 수 있다고 덧붙입니다. .out.println 나쁘게, 그렇지 않으면 log4j와 같은 좋은 로그 시스템).


2

두 번째 catch 블록에 걸리지 않습니다. 각 예외는 try 블록 내부에서만 발생합니다. 그래도 시도를 중첩 할 수 있습니다 (일반적으로 좋은 생각은 아닙니다) :

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}

비슷한 코드를 작성했습니다. 그러나 나는 확신하지 못한다. catch 블록에서 Thread.sleep ()을 호출하고 싶습니다. 그러나 Thread.sleep은 자체적으로 InterruptedException을 발생시킵니다. 당신의 예에서 보여준 것처럼 그것을하는 것이 옳은가?
riroo

1

아니오, catch는 모두 같은 try 블록을 참조하므로 catch 블록 내에서 던지는 것은 try try 블록에 의해 잡히게됩니다 (아마도 이것을 호출 한 메소드에서).


-4

이전 게시물이지만 "e"변수는 고유해야합니다.

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

5
이것은 의견이 아니라 답변이어야합니다. 실제 질문을 해결하는 방법은 없습니다. OP 질문에 대한 비판 일뿐입니다.
Derek W
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.