목차
1. 예외 계층
2. 예외 기본 규칙
3. 체크 예외
4. 언체크 예외 (런타임 예외)
1. 예외 계층
예외 처리는 아래 키워드를 사용한다
try, catch, finally, throw, thorws
- Obejct: 자바에서 기본형을 제외한 모든 것은 객체다. 예외의 최상위 부모도 Object 다.
- Throwable: 최상위 예외 객체다. 하위에 Exception 과 Error 가 있다.
- Error: 메모리 부족이나 심각한 시스템 오류와 같은 애플리케이션에서 복구 불가능한 시스템 예외다.
- 애플리케이션 개발자는 이를 잡으려고 해선 안 된다.
- Exception
- 애플리케이션 로직에서 사용할 수 있는 최상위 예외다.
- 체크 예외
- RuntimeException 을 제외하고는 모두 컴파일러가 체크하는 예외다
- 개발자가 체크 예외를 처리하지 않으면 컴파일 오류가 발생한다.
- 언체크 예외 (런타임 예외)
- 컴파일러가 체크하지 않는다.
- RuntimeException 과 그 자식 예외는 모두 언체크 예외다
주의
- 상속 관계에서 부모 타입은 자식을 담을 수 있다. 이 개념이 예외 객체에서도 적용된다.
- 상위 예외를 catch 로 잡으면, 하위 예외 까지 함께 잡는다.
- 따라서 애플리케이션 로직에서는 Throwable 예외를 잡으면 안된다. 애플리케이션 개발자가 잡으면 안 되는 Error 예외 까지 같이 잡을 수 있기 때문이다.
- 이런 이유로 애플리케이션 로직에서는Exception 부터 ‘처리가 필요한 예외’로 여기고 잡으면 된다.
2. [중요] 예외 기본 규칙
예외는 폭탄 돌리기와 같다. 잡아서 밖으로 던지거나, 처리하거나.
- 예외를 처리하지 못하면, 자신을 호출한 곳으로 예외를 던져야 한다.
- 예외를 잡거나 던질 때, 지정한 예외 뿐만 아니라 그 예외의 자식 예외도 함께 처리할 수 있다!
- 예외를 처리하지 못하고 계속 던지면 어떻게 될까?
- 자바 main() 밖으로 예외로 던지면, 예외 로그를 출력하면서 시스템이 종료된다.
3. 체크 예외
- Exception 과 그 하위 예외 모두는 컴파일러가 체크하는 체크 예외다.
- RuntimeException 은 제외다. 언체크 예외다.
- 체크 예외는 잡아서 처리하거나, 자신을 호출한 곳으로 던져야 한다. 그렇지 않으면 컴파일 오류가 발생한다.
Exception 을 상속받은 예외는 체크 예외가 된다.
- MyCheckedException 예외 클래스를 만들자.
- Exception 을 상속받으면 된다.
public class MyCheckedException extends Exception {
public MyCheckedException(String message) {
super(message);
}
}
- super(message)
- Exception 에는 message 를 보관하는 생성자가 있다.
- Throwable 의 detailMessage 에 저장된다.
- 보관한 message 를 꺼내려면, Throwable 에서 제공하는 getMessage() 메서드로 꺼낸다
throw VS throws 헷갈리지 말기
- throws : 예외를 메서드 밖으로 던질 때 쓰는 키워드다
- throw : 예외를 발생시킨다.
- new 로 객체를 생성하고 예외를 발생시킬 수 있다
- throw new MyCheckedException(”msg”)
Client class : 예외 발생하면 밖으로 던지기
- call() throws MyCheckedException → MyCheckedException 가 발생하면 메서드 밖으로 던진다
- throw new MyCheckedException("ex") → 예외 객체를 생성하여 예외를 발생시킨다.
public class Client {
public void call() throws MyCheckedException { // throws 예외 던지기
// throw 예외 발생
throw new MyCheckedException("ex");
}
}
예외를 잡아서 처리하기
- try catch 를 사용해서 예외를 잡으면 된다.
- client.call() 호출하면, MyCheckedException 이 발생하여 예외 객체가 넘어온다.
- catch 블럭 내에 예외 처리 로직을 작성한다.
public void callCatch() {
try {
client.call();
} catch (MyCheckedException e) { // 예외 처리 로직
System.out.println("예외 처리, message=" + e.getMessage());
// 예외 처리 후, 정상 흐름으로 간다.
}
System.out.println("정상 흐름");
}
catch 의 대상에 없는 예외라면?
- try 에서 잡은 예외가 catch 의 대상에 없으면 예외를 잡을 수 없다. 이때는 예외를 밖으로 던져야 한다.
- catch 대상을 RuntimeException 으로 바꿔보면, 컴파일 에러가 발생한다!
- unreported exception: exception.basic.checked.MyCheckedException;
- must be caught or declared to be thrown → 잡거나 던진다고 선언하세요.
그 예외의 자식 예외도 함께 처리하기
- MyCheckedException 은 Exception 을 상속 받은 체크 예외 클래스다.
- catch 대상을 MyCheckedException 대신 Exception 으로 바꿔서 실행해보자.
public void callCatch() {
try {
client.call();
} catch (Exception e) { // 예외 처리 로직
System.out.println("예외 처리, message=" + e.getMessage());
}
System.out.println("정상 흐름");
}
- 실행 해보면, MyCheckedException 예외를 catch 로 잡아서 처리한 것을 확인할 수 있다
예외를 메서드 밖으로 던지기 throws
- client.call() 호출하면, MyCheckedException 이 발생하여 예외 객체가 넘어온다.
- 여기서는 메서드에 선언한 throws 로 예외를 메서드 밖으로 던진다.
- 체크 예외를 밖으로 던지려면, throws 예외를 메서드에 필수로 지정해야 한다.
public void catchThrow() throws MyCheckedException {
client.call();
}
정리
- 체크 예외는 개발자가 명시적으로 처리해야 한다. 처리 안하면, 컴파일 오류 난다.
- try-catch 로 잡아서 처리 하거나, 메서드에 throws 를 지정해서 예외를 밖으로 던진다는 선언을 필수로 해야 한다.
4. 언체크 예외 (런타임 예외)
- 컴파일러가 예외를 체크하지 않는다.
- 언체크 예외는 체크 예외와 기본적으로 동일하다. 한 가지 차이가 있다. throws 를 생략할 수 있다.
- throws 를 생략해도, 자동으로 예외를 밖으로 던진다.
체크 예외 VS 언체크 예외
- 체크 예외: 예외를 처리하지 않는 경우, throws 로 던져야만 컴파일 오류가 안 난다.
- 언체크 예외: throws 를 생략해도, 자동으로 예외를 밖으로 던진다.
언체크 예외 예제코드
- 언체크 예외 클래스 RuntimeException 을 상속받은 예외는 언체크 예외가 된다.
public class MyUncheckedException extends RuntimeException{
public MyUncheckedException(String message) {
super(message);
}
}
- 언체크 예외를 발생시키는 Client 클래스
- 체크 예외와는 다르게, 메서드에 throws 키워드를 생략했다.
public class Client {
public void call() { // 메서드에 throws 키워드 생략
throw new MyUncheckedException("unckecked exception! ");
}
}
- Client 클래스 메서드를 호출하는 Service 클래스
public class Service {
Client client = new Client();
public void callCatch() { // 필요한 경우, 예외를 잡아서 처리할 수 있다.
try {
client.call();
} catch (MyUncheckedException e) {
System.out.println("예외 처리, message: " + e.getMessage());
}
System.out.println("정상 로직");
}
/**
* 예외를 잡지 않아도 컴파일 에러가 안나고, 상위로 올라간다.
* */
public void callThrow() {
client.call();
}
}
- 예외 잡아서 처리하는 메서드 callCatch() 호출하여 실행
public class UncheckedCatchMain {
public static void main(String[] args) {
Service uncheckedService = new Service();
uncheckedService.callCatch();
System.out.println("정상 종료");
}
}
- 실행 결과
예외 처리, message: my unckecked exception!
정상 로직
정상 종료
- 예외를 호출부로 던지는 callThrow() 메서드를 호출하여 실행
public class UncheckedCatchMain {
public static void main(String[] args) {
Service uncheckedService = new Service();
uncheckedService.callThrow();
System.out.println("정상 종료");
}
}
- 실행 결과
- callThrow() 메서드에 throws 키워드가 없어도, 컴파일 오류가 나지 않는다.
- callThrow() 메서드가 실행 되었고, callThrow() 에서 예외를 잡아서 처리하지 않았기 때문에 main() 으로 언체크 예외가 넘어왔다.
- main() 에서도 MyUncheckedException 을 처리하지 않고 있어서 main() 도 예외를 밖으로 던진다.
- callThrow() 까지만 실행 됬으니까, 다음 줄에 있는 “정상 종료” 출력이 실행되지 않는다.
- 이렇게 되면 예외 정보와 스택 트레이스가 출력되면서 프로그램이 종료된다.
Exception in thread "main" exception.basic.unchecked.MyUncheckedException: my unckecked exception!
at exception.basic.unchecked.Client.call(Client.java:6)
at exception.basic.unchecked.Service.callThrow(Service.java:23)
at exception.basic.unchecked.UncheckedCatchMain.main(UncheckedCatchMain.java:7)
정리
- 신경쓰고 싶지 않은 언체크 예외를 무시할 수 있다는 장점이 있다. throws 예외를 생략 가능!
- 하지만, 컴파일러를 통해 예외 누락을 잡을 수는 없다.
- 현대 애플리케이션에서는 체크 예외를 거의 쓰지 않는다.
인프런 김영한 자바 중급1 강의를 듣고 요약한 내용입니다.
728x90