Object
└── Throwable
├── Exception (S/W)
│ ├── CheckedException
│ │ ├── FileNotFoundException
│ │ ├── IOException
│ │ └── SQLException
│ │ └── InterruptedException
│ └── RuntimeException (UncheckedException)
│ ├── ArrayIndexOutOfBoundsException
│ ├── NegativeArraySizeException
│ ├── NullPointerException
│ ├── ClassCastException
│ ├── NumberFormatException
│ ├── IndexOutOfBoundsException
│ ├── ArithmeticException
│ ├── IllegalArgumentException
└── Error (System)
├── OutOfMemoryError // heap 메모리 부족
├── StackOverflowError // Stack 메모리 부족
└── NoSuchMethodError // main()이 없는데 수행한 경우
| Exception | 설명 | 종류 |
|---|---|---|
ArrayIndexOutOfBoundsException | 배열의 접근 범위를 벗어난 경우 | Unchecked |
NegativeArraySizeException | 배열의 크기를 음수로 지정한 경우 | Unchecked |
NullPointerException | 객체를 선언만 하고 생성하지 않은 상태에서 접근한 경우 | Unchecked |
ClassCastException | Reference 타입의 잘못된 형변환 시 발생 | Unchecked |
NumberFormatException | 문자열로된 데이터를 Primitive로 변환 시 잘못 변경하면 발생 | Unchecked |
IndexOutOfBoundsException | List에서 잘못된 index에 데이터를 추가한 경우 | Unchecked |
ArithmeticException | 0으로 나누는 경우 | Unchecked |
IllegalArgumentException | 메서드에 잘못된 인자를 전달한 경우 | Unchecked |
InterruptedException | 쓰레드 수행중 중단 명령에 따른 오류 | Checked |
FileNotFoundException | 지정한 경로에 파일이 없는 경우 | Checked |
IOException | 데이터를 IO하는 중 발생하는 오류 | Checked |
SQLException | DB 서버에서 데이터 처리 중 발생하는 오류 | Checked |
여기서 Error와 Exception은 다르다. Error는 StackOverflowError, OutOfMemoryError처럼 JVM 자체의 심각한 문제다. 개발자가 처리하는 것이 의미 없거나 불가능한 수준이므로, 잡으려 하지 않는 것이 맞다. 우리가 실질적으로 다루는 영역은 Exception이다.
Exception은 두 종류로 나뉜다. 기준은 RuntimeException의 상속 여부다.
Checked Exception은 RuntimeException을 상속하지 않는 Exception이다. 컴파일러가 처리를 강제한다. try-catch나 throws 없이 사용하면 빌드 자체가 실패한다.
Unchecked Exception은 RuntimeException을 상속하는 Exception이다. 컴파일러가 강제하지 않는다. 컴파일은 통과하고, 런타임에서 발생한다.
"컴파일 안 됨"과 "실행 안 됨"은 다르다. Checked Exception을 처리하지 않으면 빌드 단계에서 막힌다. Unchecked Exception은 빌드는 되고 실행 중에 터진다. 시점이 완전히 다르다.
이 구분이 생긴 이유가 중요하다. Unchecked 계열은 대부분 개발자 코드의 실수에서 발생한다. null을 잘못 다루거나, 배열 범위를 잘못 계산하거나. 이런 건 컴파일러가 강제하는 게 아니라 코드 자체를 올바르게 짜야 하는 문제다. 반면 Checked 계열인 IOException, SQLException은 파일이 존재하는지, DB 연결이 되는지 같은 외부 환경에 의존한다. 코드를 아무리 잘 짜도 파일이 없을 수 있다. 그래서 컴파일러가 "이 상황에 대한 처리 코드를 반드시 작성해"라고 강제하는 것이다.
try {
// 예외가 발생할 수 있는 코드
} catch (예외타입 변수명) {
// 예외 발생 시 처리
} finally {
// 예외 발생 여부와 무관하게 항상 실행
}
public class ExceptionHandling {
public static void main(String[] args) {
int[] scores = { 1, 2, 3 };
try {
System.out.println(scores[5]);
System.out.println("이 줄은 실행 안됨");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("배열 범위 초과" + e.getMessage());
} finally {
System.out.println("finally는 항상 실행");
}
}
}
출력 > 배열 범위 초과Index 5 out of bounds for length 3
finally는 항상 실행
흐름은 단순하다. try 블록 실행 중 예외가 발생하면, 발생 지점 이후 코드는 건너뛰고 해당 예외 타입과 일치하는 catch 블록으로 이동한다. finally는 예외 발생 여부와 관계없이 무조건 실행된다.
다중 catch를 쓸 때는 자식 타입을 먼저 써야 한다.
try {
int[] arr = {1, 2, 3};
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) { // 자식 먼저
System.out.println("배열 범위 초과");
} catch (RuntimeException e) { // 부모 나중에
System.out.println("그 외 런타임 예외");
}
부모 타입을 먼저 쓰면 자식이 절대 잡히지 않는다. 컴파일러가 이걸 에러로 잡아준다.
여기서 앞서 배운 다형성이 그대로 적용된다. catch에 부모 타입을 쓰면 자식 예외들을 전부 잡을 수 있다. catch (RuntimeException e)는 RuntimeException의 모든 자식 예외를 업캐스팅해서 받는 것이다.
이 둘은 다르다.
throw는 메서드 내부에서 예외 객체를 직접 생성해서 던지는 것이다.
throw new IllegalArgumentException("잘못된 값");
throws는 메서드 선언부에 붙이는 것으로, 이 메서드가 해당 예외를 던질 수 있다고 선언하고 처리를 호출자에게 위임하는 것이다.
public void readFile(String path) throws IOException {
FileReader fr = new FileReader(path);
}
throws를 선언한 메서드를 호출하는 쪽에서는 반드시 처리해야 한다. Checked Exception에 한해서다. Unchecked는 throws 없이도 예외가 위로 전파된다.
도메인에 맞는 예외를 직접 설계할 수 있다. Checked로 만들려면 Exception을, Unchecked로 만들려면 RuntimeException을 상속하면 된다.
public class InvalidSalaryException extends RuntimeException {
public InvalidSalaryException(String message) {
super(message); // RuntimeException 생성자에 메시지 전달
}
}
// 급여가 음수면 예외를 던지는 예시
public void setSalary(int salary) {
if (salary < 0) {
throw new InvalidSalaryException(name + "의 급여가 음수입니다: " + salary);
}
this.salary = salary;
}
| 개념 | 핵심 |
|---|---|
Error | JVM 레벨 문제, 처리 불가 |
Checked Exception | 컴파일러 강제, 외부 환경 의존 |
Unchecked Exception | 강제 없음, 개발자 코드 실수 |
throw | 메서드 내부에서 예외 객체를 직접 던짐 |
throws | 예외 처리를 호출자에게 위임 |
finally | 예외 발생 여부 무관하게 무조건 실행 |
| 다중 catch 순서 | 자식 타입 먼저, 부모 타입 나중에 |
| 사용자 정의 예외 | Exception 또는 RuntimeException 상속 |