1. 예외처리의 중요성
- 코드가 예상하지 못한 상황을 만나도 안정적으로 종료되거나 정확한 오류 메시지를 출력할 수 있도록 하기 위함
- 정상 흐름과 예외 흐름을 분리하여 코드 가독성 향상
예외 계층 구조

- Exception을 상속하면 체크 예외 (Checked)
- RuntimeException을 상속하면 언체크 예외 (Unchecked)
- Error는 JVM 내부 문제. 직접 다루지 말 것
2. 예외 기본 규칙
- 예외가 발생하면 반드시 try-catch로 잡거나, throws로 던져야 한다
- 던질 때는 throws 예외를 명시적으로 선언
public void call() throws MyCheckedException {
throw new MyCheckedException("ex");
}
3. 체크 예외(Checked Exception)
- Exception을 상속한 예외
- 컴파일러가 예외 처리 강제
- 잡거나 던지지 않으면 컴파일 오류 발생
✅ 예제
public class MyCheckedException extends Exception {
public MyCheckedException(String message) {
super(message);
}
}
package practice.exception.basic.checked;
public class Client {
public void call() throws MyCheckedException {
// 문제 상황 발생
throw new MyCheckedException("ex");
}
}
package practice.exception.basic.checked;
public class Service {
Client client = new Client();
/**
* 예외를 잡아서 처리하는 코드
*/
public void callCatch() {
try {
client.call();
} catch (MyCheckedException e) {
// 예외 처리 로직
System.out.println("예외 처리, message : " + e.getMessage());
}
// 예외를 잡으면 정상 흐름으로 복귀
System.out.println("정상 흐름");
}
/**
* 체크 예외를 밖으로 던지는 코드
* 체크 예외는 예외를 잡지 않고 밖으로 던지려면 throws 예외를 메서드에 필수로 선언해야 한다.
*/
public void catchThrow() throws MyCheckedException {
client.call();
}
}
package practice.exception.basic.checked;
public class CheckedCatchMain {
public static void main(String[] args) throws MyCheckedException {
Service service = new Service();
service.callCatch();
System.out.println("정상 종료");
service.catchThrow();
}
}
실행결과
1) 예외를 잡는 경우
예외 처리, message = ex
정상 흐름
2) 예외를 던지는 경우
Exception in thread "main" practice.exception.basic.checked.MyCheckedException: ex
at practice.exception.basic.checked.Client.call(Client.java:9)
at practice.exception.basic.checked.Service.catchThrow(Service.java:31)
at practice.exception.basic.checked.CheckedCatchMain.main(CheckedCatchMain.java:9)
- 예외 로그 출력 후 프로그램 종료
- 흐름: MyCheckedException 호출 후 예외 던짐 -> 이 흐름 반복후 마지막 메인에서 던지고 로그 출력
4. 언체크 예외 (Unchecked Exception)
- RuntimeException을 상속한 예외
- 컴파일러가 체크하지 않음
- throws 생략 가능, 필요 시 명시해도 됨
✅ 예제
package practice.exception.basic.unchecked;
/**
* RuntimeException을 상속받은 예외는 언체크 예외가 된다.
*/
public class MyUncheckedException extends RuntimeException {
public MyUncheckedException(String message) {
super(message);
}
}
package practice.exception.basic.unchecked;
public class Client {
public void call() {
throw new MyUncheckedException("ex");
}
}
package practice.exception.basic.unchecked;
/**
* Unchecked 예외는
* 예외를 잡거나. 던지지 않아도 된다.
* 예외를 잡지 않으면 자동으로 밖으로 던져진다
*/
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("정상 로직");
}
/**
* 예외를 잡지 않아도 된다. 자연스럽게 상위로 넘어감
* 체크 예외와 다르게 throw 선언을 하지 않아도 된다.
*/
public void callThrow() {
client.call();
}
}
package practice.exception.basic.unchecked;
public class UncheckedCatchMain {
public static void main(String[] args) {
Service service = new Service();
service.callCatch();
System.out.println("정상 종료");
}
}
package practice.exception.basic.unchecked;
public class UncheckedThrowMain {
public static void main(String[] args) {
Service service = new Service();
service.callThrow();
System.out.println("정상 종료");
}
}
실행 결과
예외 처리, message = ex
정상 로직
예외를 던짐 (throws 생략 가능)
MyUncheckedException: ex
→ 예외 로그 출력 후 종료
✅ 체크 vs 언체크 예외 비교
| 항목 | 체크 예외 | 언체크 예외 |
| 상속 대상 | Exception | RuntimeException |
| 컴파일 시 체크 | ✅ (필수) | ❌ (선택) |
| throws 필요 | ✅ | ❌ |
| 실무 사용 비율 | 낮음 (복잡한 외부 작업) | 높음 (버그, 개발자 실수 등) |
- 체크 예외는 복잡하고 중요한 외부 리소스 처리에 적합 (예: JDBC, File I/O)
- 언체크 예외는 버그나 잘못된 호출을 잡는 데 적합 (예: NullPointerException, IllegalArgumentException)
출처 : 김영한 자바 중급 1편
'Java' 카테고리의 다른 글
| Java 중급 1 - 예외처리(4) (0) | 2025.05.10 |
|---|---|
| Java 중급 1 - 예외처리(3) (0) | 2025.05.05 |
| Java 중급 1 - 예외처리(1) (0) | 2025.05.02 |
| Java 중급 1 - 중첩클래스, 내부 클래스(4) (0) | 2025.04.21 |
| Java 중급 1 - 중첩클래스, 내부 클래스(3) (0) | 2025.04.16 |