Java

자바의 예외 처리

목적

자바의 예외 처리에 대해 학습하세요.

학습할 것

  1. 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
  2. 자바가 제공하는 예외 계층 구조
  3. Exception과 Error의 차이는?
  4. RuntimeException과 RE가 아닌 것의 차이는?
  5. 커스텀한 예외 만드는 방법

예외란

프로그램 실행 중 발생하는 오류 중, 개발자가 처리해서 프로그램을 계속 진행시킬 수 있는 것을 예외라 한다.
처리하지 않으면 JVM이 프로그램을 종료시킨다.

1. 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)


try-catch

try {
    int result = 10 / 0;          // 예외 발생 지점
} catch (ArithmeticException e) {
    System.out.println("0으로 나눌 수 없습니다: " + e.getMessage());
}
  • try 블록에서 예외가 발생하면 그 지점에서 즉시 빠져나와 일치하는 catch 블록으로 이동한다.
  • 따라서 예외 발생 이후의 try 블록 코드는 실행되지 않는다.
  • catch 블록은 여러 개 둘 수 있고 위에서부터 순서대로 매칭된다.
  • 상위 타입인 Exception을 먼저 쓰면 아래쪽은 도달할 수 없어 컴파일 에러가 난다.

finally

try {
    // ...
} catch (Exception e) {
    // ...
} finally {
    // 예외 발생 여부와 관계없이 항상 실행
}
  • 예외가 나든 안 나든 반드시 실행된다.
  • try나 catch 안에 return이 있어도 실행되기 때문에 자원 반납(close)에 사용한다.
  • 자바 7부터는 try-with-resources 가 더 깔끔하다.
try (BufferedReader br = new BufferedReader(new FileReader("a.txt"))) {
    return br.readLine();
}   // close() 자동 호출

throw 와 throws

// throw : 예외를 직접 발생시킨다
if (age < 0) {
    throw new IllegalArgumentException("나이는 음수일 수 없습니다.");
}

// throws : 처리하지 않고 호출한 쪽으로 미룬다
void readFile() throws IOException {
    // 여기서 처리 안 함 -> 호출한 쪽이 책임진다
}

이름이 비슷해 헷갈리는데 throw 는 던진다, throws 는 던질 수 있다고 알린다 로 구분하면 된다.

2. 자바가 제공하는 예외 계층 구조


  • 모든 예외의 조상은 Throwable 이며, 여기서 ErrorException 으로 갈린다.
  • throw 로 던질 수 있는 것은 Throwable 을 상속받은 것뿐이다.

3. Exception과 Error의 차이는?


구분 Exception Error
성격 프로그램 로직상의 문제 JVM/시스템 수준의 문제
복구 가능성 가능 사실상 불가
처리 여부 개발자가 처리해야 함 처리해도 의미 없음
IOException, NullPointerException OutOfMemoryError, StackOverflowError

핵심은 개발자가 손쓸 수 있느냐다. 메모리가 부족해 OutOfMemoryError 가 났는데 catch 로 잡아봐야 할 수 있는 게 없다.
그래서 Error 는 잡지 않는 것이 정상이다.

4. RuntimeException과 RE가 아닌 것의 차이는?


흔히 Unchecked / Checked 예외로 부른다.

구분 RuntimeException 계열 (Unchecked) 그 외 (Checked)
컴파일러 검사 안 함 함 (처리 안 하면 컴파일 에러)
처리 의무 선택 강제 (try-catch 또는 throws)
의미 개발자 실수로 발생 발생을 예상해야 하는 상황
NullPointerException, IndexOutOfBoundsException IOException, SQLException
  • Checked : 파일이 없을 수도, 네트워크가 끊길 수도 있으니 미리 대비하라는 것
  • Unchecked : null 체크를 안 해서 NPE 가 나듯 코드를 잘못 쓴 것

그래서 Unchecked 는 catch 로 막는 게 아니라 코드를 고쳐서 발생하지 않게 하는 것이 맞다.

5. 커스텀한 예외 만드는 방법


기존 예외 클래스를 상속받으면 되고, 어느 쪽을 상속하느냐가 Checked/Unchecked 를 결정한다.

// Unchecked - RuntimeException 상속
public class InsufficientBalanceException extends RuntimeException {

    public InsufficientBalanceException(String message) {
        super(message);
    }

    public InsufficientBalanceException(String message, Throwable cause) {
        super(message, cause);   // 원인 예외를 묶어서 전달
    }
}
// Checked - Exception 상속
public class MailSendException extends Exception {
    public MailSendException(String message) {
        super(message);
    }
}

사용할 때는 아래처럼 던진다.

if (balance < amount) {
    throw new InsufficientBalanceException(
        "잔액 부족: 현재 " + balance + "원, 요청 " + amount + "원");
}

만들 때 주의할 점

  • 이름에 상황이 드러나게 짓는다. MyException 보다 InsufficientBalanceException
  • 원인 예외를 버리지 않는다. super(message, cause) 로 넘겨야 스택트레이스가 끊기지 않는다.
  • 메시지에 판단 근거를 담는다. 위처럼 실제 값을 넣으면 로그만 보고도 원인을 알 수 있다.
  • 만들기 전에 표준 예외로 충분한지 본다. IllegalArgumentException, IllegalStateException 으로 되는 경우가 많다.

Reference

남궁성. Java의 정석