GOAL
자바의 예외 처리에 대해 학습하세요.
학습할 것 (필수)
- 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
- 자바가 제공하는 예외 계층 구조
- Exception과 Error의 차이는?
- RuntimeException과 RE가 아닌 것의 차이는?
- 커스텀한 예외 만드는 방법
자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
예외(exception)란 ?
‘예외적인 이벤트’의 약자로 프로그램의 정상적인 흐름은 방해하는 이벤트라고 정의 한다
오라클
에외 전체 흐름도
try
- try블럭 내에서 예외가 발생한 경우, 해당 되는 catch 블록을 실행 한다.
- 일치하는 catch블럭을 찾으면, 그 catch블럭 내의 문장들을 수행
- 일치하는 catch블럭이 없으면, 처리하지 못하고 그대로 잔행
catch
- try블럭에서 발생한 예외와 catch블록에 있는 예외를 비교하여 일치하는 예외 블록시 실행 된다
1
2
3
4
5
6
7
8
9
public class TryCatchFinally {
public static void main(String[] args) {
try{
...처리작업
}catch (Exception e){
...예외발생시 처리작업
}
}
}
컴파일소스
1
2
3
4
5
6
7
8
9
10
11
public class TryCatchFinally {
public TryCatchFinally();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: return
}
추가
catch블럭(자바7)
- 기호(|)를 통해 하나의 catch블럭으로 통합
- 기호(|)로 연결된 예외 클래스가 부모와 자식 관계에 있다면 컴파일 에러가 발생
- 참조변수 e는 상수 라서 값을 변경할 수 없다
1
2
3
4
5
try {
...
} catch (ExceptionA | ExceptionB e) {
e.printStackTrace();
}
finally
- 블록내 처리후 반드시 실행되는 블럭 io나 connect 등 사용후 종료 하는 자원이 있을 경우 사용
- finally 블록에서 예외 발생시, catch블록의 예외 추적이 불가하다.
- 이런 문제는 자바7에서 try-with-resources로 해결할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
spublic class TryCatchFinally {
public static void main(String[] args) {
try{
...처리작업
}catch (Exception e){
...예외발생시 처리작업
}finally {
...항상 실행 작업
}
}
}
컴파일소스
1
2
3
4
5
6
7
8
9
10
11
12
public class TryCatchFinally {
public TryCatchFinally();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: return
}
ch
try-with-resources(자바7)
- 대상이 되는 메소드는 AutoCloseable 인터페이스를 구현해야 한다.
- try블록이 종료 되면, AutoCloseable 의 close()메소드의 구현체가 실행된다.
- try-catch문과 AutoCloseable.close()에서 모두 예외가 발생되면, 두 예외가 동시에 발생할 수는 없기 때문에 close()에서 발생되는 예외는
억제된 예외
로 처리되어 실제 발생한 예외(try-catch문에서 발생한 예외)에 저장된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TryCatchResource {
public static void main(String[] args){
try(TryCatchResourceMethod c = new TryCatchResourceMethod()){
} catch (Exception e) {
}
}
}
class TryCatchResourceMethod implements AutoCloseable {
@Override
public void close() {
System.out.println("always exec");
}
}
컴파일 소스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TryCatchResource {
public TryCatchResource();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #7 // class TryCatchResourceMethod
3: dup
4: invokespecial #9 // Method TryCatchResourceMethod."<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #10 // Method TryCatchResourceMethod.close:()V
12: goto 16
15: astore_1
16: return
Exception table:
from to target type
0 12 15 Class java/lang/Exception
}
실행 순서를 보면
- 0번에서 생성한 클래스를 7번라인에서 저장
- 7번라인에서 저장한 클래스를 8번 라인에서 로드
- 8번라인에서 로드한 클래스의 close()를 9번 라인에서 실행
예외 처리는 Exception table 테이블을 이용하여, 12번 라인의 목적이 메모값을 변경 (종료인 16라인에서 Exception 클래스 가 있는 15번 라인으로 이동)
output
1
always exec
getSuppressed,addSuppressed
- Throwable에 추가된 getSuppressed메소드를 이용하면 예외를 가져올 수도 있다.
- Throwable에 추가된 addSuppressed메소드를 이용하면 예외를 추가할 수도 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Throwable implements Serializable {
public final synchronized Throwable[] getSuppressed() {
if (suppressedExceptions == SUPPRESSED_SENTINEL ||
suppressedExceptions == null)
return EMPTY_THROWABLE_ARRAY;
else
return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
}
public final synchronized void addSuppressed(Throwable exception) {
if (exception == this)
throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE, exception);
Objects.requireNonNull(exception, NULL_CAUSE_MESSAGE);
if (suppressedExceptions == null) // Suppressed exceptions not recorded
return;
if (suppressedExceptions == SUPPRESSED_SENTINEL)
suppressedExceptions = new ArrayList<>(1);
suppressedExceptions.add(exception);
}
}
throw
- throw키워드를 이용해서 고의로 예외를 발생 가능
1 2
Exception e = new Exception("예외"); throw e;
throws
- throws키워드를 이용해서 예외를 호출한 메소드로 넘길 수 있다.
하지마세요 - throws키워드를 사용할 경우, 예외 처리를 위한 Exception table 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TryCatchResource {
public static void main(String[] args) {
parent();
}
public static void parent(){
try{
child();
} catch (Exception e) {
System.out.println("parent");
e.printStackTrace();
}
}
private static void child() throws Exception {
throw new Exception("child Exception");
}
}
컴파일소스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class TryCatchResource {
public TryCatchResource();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #7 // Method parent:()V
3: return
public static void parent();
Code:
0: invokestatic #12 // Method child:()V
3: goto 19
6: astore_0
7: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream;
10: ldc #23 // String parent
12: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
15: aload_0
16: invokevirtual #30 // Method java/lang/Exception.printStackTrace:()V
19: return
Exception table:
from to target type
0 3 6 Class java/lang/Exception
}
컴파일 소스를 보면 예외 처리를 위한 Exception table이 생긴걸 알 수 있다.
output
1
2
3
4
5
parent
java.lang.Exception: child Exception
at TryCatchResource.child(TryCatchResource.java:18)
at TryCatchResource.parent(TryCatchResource.java:10)
at TryCatchResource.main(TryCatchResource.java:5)
자바가 제공하는 예외 계층 구조
https://madplay.github.io/post/java-checked-unchecked-exceptions
- Exception과 Error는 Throwable이라는 클래스를 상속받고 있으며 Throwable은 Object를 직접 상속받음
Exception과 Error의 차이는?
- Throwable은 Error와 Exception이라는 두 개의 하위 클래스를 갖는데, 필요한 곳에서 Exception클래스를 확인하고 수정하는 것으로 개발자가 직접 처리 할 수 있다
- Error는 OutOfMemoryError나 NoClassDefFoundError클래스처럼 개발자 스스로 처리 할 수 있는것이 아니다.
RuntimeException과 RE가 아닌 것의 차이는?
- 예외는 ‘런타임 예외(runtime exception)’이거나 ‘확인해야 하는 예외(checked exception)’ 두 가지로 구분
- 런타임 예외는 모두 RuntimeException의 하위 클래스고, 확인해야 하는 예외는 모두 다른 예외
- 확인해야 하는 예외(checked exception)를 처리하는 메소드(또는 생성자)를 사용할 때는 메소드 정의에 명시적으로 예외가 정의되어야 하며, 따라 코드를 호출하는 모든 호출자들은 해당 예외를 처리 즉, 메소드의 호출자에게 전달하거나 try/catch/finally문으로 예외를 적절히 처리 참고
커스텀한 예외 만드는 방법
- 기존 정의된 예외 클래스 외에 필요에 따라 새로운 예외 클래스를 정의
- 보통 Exception클래스로부터 상속받는 클래스를 만들지만, 필요에 따라 알맞은 예외 클래스를 선택 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CustomException extends Exception {
private final int ERROR_CODE;
CustomException(String message, int errorCode) {
super(message);
this.ERROR_CODE = errorCode;
}
CustomException(String message) {
this(message, 100);
}
public int getErrorCode() {
return ERROR_CODE;
}
}
컴파일소스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
ublic class CustomException extends java.lang.Exception {
CustomException(java.lang.String, int);
Code:
0: aload_0
1: aload_1
2: invokespecial #1 // Method java/lang/Exception."<init>":(Ljava/lang/String;)V
5: aload_0
6: iload_2
7: putfield #7 // Field ERROR_CODE:I
10: return
CustomException(java.lang.String);
Code:
0: aload_0
1: aload_1
2: bipush 100
4: invokespecial #13 // Method "<init>":(Ljava/lang/String;I)V
7: return
public int getErrorCode();
Code:
0: aload_0
1: getfield #7 // Field ERROR_CODE:I
4: ireturn
}
참고
Comments powered by Disqus.