자바 언어·플랫폼 용어 사전
예외·자원checked 예외 · unchecked 예외 · RuntimeException · Error

예외 계층

Throwable 아래 Error·Exception·RuntimeException 구조. 검사 여부가 컴파일러 강제를 가른다.

Throwable 아래의 상속 구조가 컴파일러가 무엇을 강제하는지를 결정한다.

Throwable
 ├─ Error              JVM 수준의 회복 불가 — OutOfMemoryError, StackOverflowError
 │                     잡지 않는다 (잡아도 할 수 있는 게 없다)
 └─ Exception          검사 예외(checked) — 반드시 처리하거나 선언해야 한다
     └─ RuntimeException  비검사(unchecked) — 강제되지 않는다
                          NPE, IllegalArgumentException, IllegalStateException

어느 쪽으로 만들 것인가

  • checkd 로 만든다 — 호출자가 '합리적으로 복구할 수 있는' 상황
    • 예) 파일이 없다 → 기본값으로 대체하거나 다시 물어본다

unchecked 로 만든다 프로그래밍 오류 — 계약 위반

  • 예) null 을 넘겼다, 상태가 잘못됐다, 인덱스가 범위 밖

판단 기준은 "호출자가 이걸 잡아서 뭔가 다르게 할 수 있는가" 다 할 수 있는 게 없다면 checked 로 강제해 봐야 잡고 무시할 뿐이다

예외를 삼키는 안티패턴

try { ... } catch (Exception e) { }              // 최악 — 흔적조차 없다
try { ... } catch (Exception e) { e.printStackTrace(); }  // 로그 체계 밖으로 샌다

// 감싸 던질 때는 원인을 반드시 붙인다
catch (SQLException e) {
    throw new DataAccessException("주문 조회 실패 id=" + id, e);  // ← cause
}

원인을 붙이지 않으면 Caused by: 사슬이 끊겨 진짜 발생 지점을 영영 못 찾는다.

InterruptedException 은 특별하다

catch (InterruptedException e) {
    Thread.currentThread().interrupt();  // 인터럽트 상태를 복원한다
    throw new RuntimeException(e);
}

잡으면 인터럽트 플래그가 지워지므로, 복원하지 않으면 상위의 종료 신호가 사라진다 — 스레드 풀이 안 꺼지는 흔한 원인이다.

스택트레이스는 비싸다

예외 생성 비용의 대부분은 fillInStackTrace() 다 스택을 훑어 프레임을 배열로 만든다 — 깊은 스택에서 수 µs

그래서 '흐름 제어에 예외를 쓰지 마라' 는 조언이 나온다

  • 검증 실패마다 예외를 던지는 API 가 초당 수만 건을 처리하면
  • 예외 생성만으로 CPU 를 태운다

정말 필요하면 스택트레이스를 끄는 생성자를 쓴다

  • super(msg, cause, suppression, writableStackTrace=false)

같은 예외가 반복되면 JVM 이 지운다

-XX:+OmitStackTraceInFastThrow (기본 켜짐)

NPE 같은 예외가 같은 지점에서 반복되면 JIT 이 스택트레이스 없는 미리 만들어 둔 인스턴스로 바꿔 던진다

  • 증상 — "java.lang.NullPointerException" 한 줄만 찍히고 원인 위치가 없다
    • 운영에서만, 그것도 한참 지나서야 나타난다 (JIT 컴파일 후)
  • 대응 — -XX:-OmitStackTraceInFastThrow 로 끄고 재현

Helpful NullPointerException (Java 14+)

a.getB().getC().getD() 에서 무엇이 null 인지 알려 준다

  • "Cannot invoke String.length() because the return value of
    • Order.getCustomer() is null"

Java 15 부터 기본 활성화다. 이것만으로 디버깅 시간이 크게 준다

면접 함정

  • "checked 예외가 더 안전하다" → 강제만 될 뿐, 잡고 무시하면 오히려 더 위험하다.
  • "Error도 잡아서 복구한다" → OOM 이후의 코드는 신뢰할 수 없다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 컬렉션·예외·리소스