Java/일반

Java의 에러(Error)와 예외(Exception), throws와 throw의 차이

개발자 May 2024. 10. 8.

프로그램이 실행 중 어떤 원인에 의해 오작동을 하거나 비정상적으로 종료되는 경우가 있다. 이런 경우 흔히들 ‘에러 났다!’라고 표현하곤 하는데, 자바에서는 이렇게 프로그램 실행 시(runtime)에 발생할 수 있는 오류를 에러(Error)와 예외(Exception)로 구분하여 처리한다.

에러가 발생하면 프로그램 종료를 막기는 어렵지만, 예외는 발생하더라도 프로그래머가 이를 적절히 처리하도록 미리 코드상에서 처리를 해두어 비정상적인 문제를 방지할 수 있다.

  • 에러(Error):
    • 프로그램 코드로 수습할 수 없는 심각한 오류를 의미한다.
    • JVM의 내부 문제나 시스템 수준의 오류 등으로 인해 발생하며, 프로그램이 정상적으로 복구되기 어렵다.
    • 예: StackOverflowError, OutOfMemoryError.
  • 예외(Exception):
    • 프로그램 코드로 예외적인 상황을 처리할 수 있는 오류를 의미한다.
    • 예외가 발생하더라도 적절한 예외 처리 코드를 통해 프로그램의 비정상 종료를 방지할 수 있다.

이제 자바의 예외 처리와 관련된 Throwable, Exception 클래스와 throw, throws 키워드의 차이점을 살펴보자.


Throwable과 Exception

1. Throwable 클래스

자바의 예외 처리는 Throwable 클래스에서 시작된다. Throwable은 모든 예외(Exception)와 오류(Error)의 최상위 클래스로, 예외와 오류 객체의 공통적인 특징을 정의한다.

java.lang.Object
  └─ java.lang.Throwable
      ├─ java.lang.Exception     // 예외 클래스
      └─ java.lang.Error         // 오류 클래스
  • 자바에서 모든 오류와 예외의 최상위 클래스이다.
  • JVM이 예외를 던지거나, 프로그래머가 throw 키워드를 사용하여 예외를 던질 때, Throwable의 인스턴스 또는 그 하위 클래스만이 사용될 수 있다.
  • catch 구문에서도 Throwable 또는 그 하위 클래스만 예외 타입으로 받을 수 있다.

예외 발생 시 Throwable의 주요 역할

  • 스택 트레이스(Execution Stack) 정보 제공:
    • printStackTrace(): 예외 발생 당시의 스택 추적 정보를 콘솔에 출력
    • Throwable 객체는 예외가 발생한 시점의 실행 스택 상태를 포함하며, 예외 발생 당시의 메서드 호출 순서를 기록한다.
    • 이를 통해 예외의 발생 지점을 추적하고 디버깅할 수 있다.
  • 메시지 문자열:
    • getMessage(): 예외나 오류에 대한 설명 메시지 반환
    • 예외가 발생한 이유를 설명하는 추가적인 메시지를 포함할 수 있다.
  • 원인(cause) 추적 및 예외 체인:
    • getCause(): 예외의 근본 원인을 반환하여 예외 체인 추적
    • Throwable 객체는 다른 예외나 오류를 원인(cause)으로 포함할 수 있다.
    • 이를 통해 예외 체인(exception chaining)을 형성하여, 최초 예외 발생 원인부터 연속적인 예외 발생을 추적할 수 있다.
    • 예외 체인을 사용하면, 다중 계층에서 발생한 예외를 하나의 흐름으로 관리하여, 구체적인 예외의 원인을 추적하기가 용이해진다.

2. Exception 클래스

Exception 클래스는 Throwable의 자식 클래스로, 예외적인 상황을 처리하기 위한 클래스이다.

  • Throwable 클래스의 하위 클래스로, 회복 가능한 예외적인 상황을 나타낸다.
  • 대부분의 예외 처리(try-catch)는 Exception 클래스와 그 하위 클래스에서 이루어진다.

Exception의 두 가지 종류

  1. 체크드 예외 (Checked Exception):
    • 컴파일 시점에 예외 처리를 강제하는 예외.
    • Exception 클래스를 상속하지만, RuntimeException을 상속하지 않는 예외.
    • 예: IOException, SQLException.
  2. 언체크드 예외 (Unchecked Exception):
    • RuntimeException을 상속하는 예외로, 컴파일러가 예외 처리를 강제하지 않는다.
    • 주로 런타임 시점에 발생하며, 프로그램의 논리적 오류나 잘못된 접근을 나타낸다.
    • 예: NullPointerException, IndexOutOfBoundsException.

Throw와 Throws

1. Throw 키워드

  • 예외를 직접 발생시킬 때 사용하는 키워드.
  • throw 키워드를 사용하여 예외 객체를 생성하고 던질 때 메서드의 실행이 즉시 종료되며, 발생한 예외는 상위 메서드로 전달된다.
  • 프로그래머가 의도적으로 특정 조건이 맞지 않을 때 예외를 발생시키기 위해 사용된다.
throw new 예외클래스("예외 메시지");
public class Main {
    public static void main(String[] args) {
            // try-catch 블록을 통한 예외 처리
        try {
            validateAge(15);  // 유효하지 않은 나이로 인해 예외 발생
        } catch (IllegalArgumentException e) {
            System.out.println("Exception caught: " + e.getMessage());
        }
    }

    // 특정 조건에 맞지 않을 때 예외를 던짐
    public static void validateAge(int age) {
        if (age < 18) {
            // 예외 발생: 메서드 내에서 직접 예외 객체를 생성하여 던짐
            throw new IllegalArgumentException("나이는 18세 이상이어야 합니다.");
        }
        // 예외가 발생할 경우 해당 라인에서 즉시 종료되어 아래 라인까지 도달하지 않음
        System.out.println("나이가 유효합니다: " + age);
    }
}
  • validateAge 메서드에서 특정 조건(나이 18세 미만) 일 때 의도적으로 예외를 발생시키고, 이를 throw 키워드를 사용해 상위 호출 메서드로 전달한다.
  • main 메서드에서는 try-catch 블록을 통해 예외를 처리한다.

2. Throws 키워드

  • throws메서드 선언부에서 사용되며, 해당 메서드가 특정 예외를 던질 수 있음을 명시한다.
  • 주로 체크드 예외가 발생할 수 있는 메서드에서 사용하며, 호출자는 예외 처리를 강제받는다.
반환타입 메서드명(매개변수 목록) throws 예외클래스1, 예외클래스2 .. {}

예외 처리를 강제받아 throws 키워드가 붙어있는 메서드는 반드시 try-catch 블록 내에서 호출되어 catch 블록에서 떠넘겨받은 예외를 처리해야 한다. (혹은 한번 더 throws로 호출한 곳으로 넘길 수도 있다)

public void method1() {
        try {
                method2();
        } catch (ClassNotFoundException e) {
                // 예외 처리 코드
                System.out.println("예외 발생");
        }
}

// 예외를 호출한 곳으로 한번 더 떠넘기는 것도 가능하다.
// public void method1() throws ClassNotFoundException {
//         method2();
// }

public void method2() throws ClassNotFoundException {
        Class clazz = Class.forname("java.lang.String2");
}
  • method2 메서드는 throw ClassNotFoundException을 사용하여 체크드 예외를 호출자에게 전달한다.
  • 호출자 method1 메서드는 method2를 호출할 때 try-catch 블록을 사용하여 예외를 처리하거나, mehotd1 메서드를 호출한 호출자에게 throws 키워드를 통해 한번 더 떠넘길 수도 있다.

Throw vs Throws 정리

특징 throw throws
용도 예외를 직접 발생시킬 때 사용 메서드가 예외를 던질 수 있음을 선언
위치 메서드 내부 메서드 선언부
역할 예외 객체를 직접 던짐 해당 메서드가 어떤 예외를 던질 수 있는지 선언
예외 종류 RuntimeException, CheckedException 모두 가능 CheckedException의 강제 예외 처리 수단으로 사용, RuntimeException도 사용 가능
사용 개수 여러 개 throw 가능 메서드 선언부에 여러 예외 선언 가능

Reference

도서

  • Java의 정석(남궁성 저)
  • 이것이 자바다(신용권 저)

댓글