코딩쿠의 코드 연대기

Java 예외 처리 본문

코딩스터디/JAVA스터디

Java 예외 처리

코딩쿠 2024. 11. 15. 22:21

학습목표

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

Java에서 예외 처리 방법

Java에서 예외(Exception)는 프로그램 실행 중 발생할 수 있는 예상치 못한 오류 상황을 의미합니다. 예외는 프로그램의 정상적인 흐름을 방해하기 때문에 적절히 처리하지 않으면 프로그램이 비정상적으로 종료될 수 있습니다. Java는 예외 처리를 위한 다양한 메커니즘을 제공하며, try, catch, throw, throws, finally 키워드를 사용하여 예외를 처리할 수 있습니다.

1. try-catch 블록

try-catch 블록은 예외가 발생할 가능성이 있는 코드를 try 블록 안에 넣고, 예외 발생 시 처리할 코드를 catch 블록 안에 넣는 방식입니다.

Java

try {
    // 예외가 발생할 가능성이 있는 코드
    int result = 10 / 0; // 0으로 나누는 예외 발생
} catch (ArithmeticException e) {
    // ArithmeticException 예외 발생 시 처리할 코드
    System.out.println("0으로 나눌 수 없습니다.");
}

try 블록 내에서 예외가 발생하면, JVM은 해당 예외에 맞는 catch 블록을 찾습니다. catch 블록의 매개변수는 try 블록에서 발생한 예외 객체를 받아 처리합니다.

2. throw 키워드

throw 키워드는 예외를 명시적으로 발생시키는 데 사용됩니다. 주로 개발자가 특정 조건에서 예외를 발생시켜 프로그램의 흐름을 제어하고자 할 때 사용합니다.

Java

public void validateAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("나이는 0보다 작을 수 없습니다.");
    }
}

위 예제에서 validateAge 메서드는 age가 0보다 작으면 IllegalArgumentException 예외를 발생시킵니다.

3. throws 키워드

throws 키워드는 메서드 선언부에 사용되어 해당 메서드에서 발생할 수 있는 예외를 호출하는 쪽으로 넘기는 역할을 합니다. 즉, 메서드 내에서 예외를 직접 처리하지 않고 호출한 쪽에서 처리하도록 위임하는 것입니다.

Java

public void readFile(String fileName) throws IOException {
    // 파일을 읽는 코드 (IOException 발생 가능)
}

위 예제에서 readFile 메서드는 IOException 예외를 throws 합니다. 이 메서드를 호출하는 쪽에서는 try-catch 블록을 사용하여 IOException 예외를 처리해야 합니다.

4. finally 블록

finally 블록은 예외 발생 여부와 관계없이 항상 실행되는 코드 블록입니다. 주로 자원 해제와 같은 작업에 사용됩니다.

Java

try {
    // 예외가 발생할 가능성이 있는 코드
} catch (Exception e) {
    // 예외 처리 코드
} finally {
    // 항상 실행되는 코드 (예: 파일 닫기, 연결 해제)
}

finally 블록은 try 블록 내에서 예외가 발생하든 발생하지 않든, catch 블록에서 예외가 처리되든 되지 않든 항상 실행됩니다.

예외 처리 예제

Java

public class ExceptionExample {
    public static void main(String[] args) {
        try {
            int[] numbers = new int[5];
            numbers[5] = 10; // ArrayIndexOutOfBoundsException 발생
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("배열의 범위를 벗어났습니다.");
        } finally {
            System.out.println("프로그램 종료");
        }
    }
}

위 예제에서는 try 블록 내에서 배열의 범위를 벗어나는 접근을 시도하여 ArrayIndexOutOfBoundsException 예외가 발생합니다. 이 예외는 catch 블록 추가 정보

  • Java는 다양한 종류의 예외 클래스를 제공합니다. 각 예외 클래스는 특정 오류 상황을 나타냅니다.
  • 예외 클래스는 계층 구조를 가지며, 상위 클래스는 하위 클래스의 예외를 처리할 수 있습니다.
  • try-catch 블록은 여러 개의 catch 블록을 가질 수 있으며, 각 catch 블록은 특정 예외를 처리합니다.
  • try-with-resources 문을 사용하면 자원을 자동으로 해제할 수 있습니다.

참고 자료

Java가 제공하는 예외 계층 구조

Java에서 예외는 계층 구조로 관리됩니다. 모든 예외 클래스는 java.lang.Throwable 클래스를 최상위 클래스로 하여 상속 관계를 갖습니다.

Throwable 클래스는 두 가지 주요 하위 클래스를 가집니다.

  • Error: 시스템에 심각한 문제가 발생하여 복구가 어려운 경우 발생하는 오류입니다. 예를 들어, 시스템 메모리 부족, 스택 오버플로우 등이 있습니다. 일반적으로 프로그램에서 Error를 처리할 수는 없으며, 발생 시 프로그램은 비정상적으로 종료됩니다.
  • Exception: 프로그램에서 발생할 수 있는 예외적인 상황을 나타냅니다. 프로그램 코드에서 예외 처리를 통해 복구를 시도할 수 있습니다. Exception 클래스는 다시 두 가지 유형으로 나뉩니다.
    • Checked Exception: 컴파일 시점에 검사되는 예외입니다. 예외 처리 코드를 명시적으로 작성하지 않으면 컴파일 오류가 발생합니다. IOException, SQLException 등이 이에 속합니다.
    • Unchecked Exception: 컴파일 시점에 검사되지 않는 예외입니다. 주로 프로그래머의 실수로 인해 발생하며, RuntimeException 클래스의 하위 클래스입니다. NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException 등이 이에 속합니다.

다음은 Java 예외 계층 구조를 간략하게 표현하면

Throwable
  - Error
      - OutOfMemoryError
      - StackOverflowError
      - ...
  - Exception
      - Checked Exception
          - IOException
              - FileNotFoundException
          - SQLException
          - ...
      - Unchecked Exception (RuntimeException)
          - NullPointerException
          - ArrayIndexOutOfBoundsException
          - IllegalArgumentException
          - ArithmeticException
          - ...

예외 클래스 계층 구조를 이해하는 것은 중요합니다. 왜냐하면 예외 처리 코드를 작성할 때, 상위 클래스의 예외를 catch하면 해당 클래스의 모든 하위 클래스 예외를 처리할 수 있기 때문입니다. 예를 들어, Exception 클래스를 catch하면 모든 Checked Exception과 Unchecked Exception을 처리할 수 있습니다.

Java Exception과 Error의 차이는?

Java에서 ExceptionError는 모두 Throwable 클래스를 상속받지만, 프로그램에서 발생하는 문제의 심각성과 처리 방식에서 중요한 차이가 있습니다.

1. Exception

  • 정의: 프로그램 실행 중 발생할 수 있는 예외적인 상황을 나타냅니다. 주로 프로그래머의 실수나 예상치 못한 입력, 외부 시스템 오류 등으로 인해 발생합니다.
  • 처리 방식: try-catch 블록을 사용하여 예외를 처리하고 프로그램의 정상적인 실행을 유지할 수 있습니다.
  • 종류:
    • Checked Exception: 컴파일 시점에 검사되는 예외입니다. 예외 처리 코드를 명시적으로 작성하지 않으면 컴파일 오류가 발생합니다. IOException, SQLException 등이 이에 속합니다.
    • Unchecked Exception: 컴파일 시점에 검사되지 않는 예외입니다. 주로 프로그래머의 실수로 인해 발생하며, RuntimeException 클래스의 하위 클래스입니다. NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException 등이 이에 속합니다.

2. Error

  • 정의: 시스템에 심각한 문제가 발생하여 복구가 어려운 경우 발생하는 오류입니다.
  • 처리 방식: 일반적으로 프로그램에서 Error를 처리할 수 없으며, 발생 시 프로그램은 비정상적으로 종료됩니다.
  • 종류: OutOfMemoryError, StackOverflowError, ThreadDeath 등이 있습니다.
  • 발생 원인: 시스템 메모리 부족, 스택 오버플로우, JVM 내부 오류 등 시스템 자체의 문제로 인해 발생합니다.

Exception과 Error의 주요 차이점 요약

구분 Exception Error
심각도 비교적 덜 심각 매우 심각
처리 가능성 처리 가능 처리 불가능
발생 원인 프로그래머 실수, 예상치 못한 입력, 외부 시스템 오류 시스템 자체의 문제
예시 IOException, NullPointerException OutOfMemoryError, StackOverflowError

간단한 비유

  • Exception: 자동차 운전 중 타이어가 펑크 나는 상황과 같습니다. 펑크 난 타이어를 교체하면 다시 운전을 계속할 수 있습니다.
  • Error: 자동차 운전 중 엔진이 폭발하는 상황과 같습니다. 엔진 폭발은 심각한 문제이며, 운전을 계속할 수 없습니다.

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

Java에서 Exception은 크게 두 가지 종류로 나뉘는데, 바로 RuntimeExceptionRE가 아닌 것 (Checked Exception)입니다. 이 둘의 가장 큰 차이점은 컴파일러가 예외 처리를 강제하는지 여부에 있습니다.

1. RuntimeException (Unchecked Exception)

  • 정의: 프로그램 실행 중에 발생하는 예외로, 주로 프로그래머의 실수로 인해 발생합니다.
  • 컴파일러 검사: 컴파일 시점에 예외 처리 코드를 검사하지 않습니다. 즉, try-catch 블록으로 감싸거나 throws 키워드를 사용하여 예외를 선언하지 않아도 컴파일 오류가 발생하지 않습니다.
  • 처리 방식: 예외 처리를 강제하지 않기 때문에 개발자가 필요에 따라 예외 처리 코드를 작성할 수 있습니다.
  • 예시: NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException, ArithmeticException

2. RE가 아닌 것 (Checked Exception)

  • 정의: 외부 요인이나 예상치 못한 상황으로 인해 발생하는 예외입니다.
  • 컴파일러 검사: 컴파일 시점에 예외 처리 코드를 검사합니다. 즉, try-catch 블록으로 감싸거나 throws 키워드를 사용하여 예외를 선언해야 합니다. 그렇지 않으면 컴파일 오류가 발생합니다.
  • 처리 방식: 예외 처리를 강제하기 때문에 개발자는 반드시 예외 처리 코드를 작성해야 합니다.
  • 예시: IOException, SQLException, ClassNotFoundException

RuntimeException과 Checked Exception의 주요 차이점 요약

구분 RuntimeException Checked Exception
컴파일러 검사 X O
예외 처리 강제 X O
발생 시점 주로 실행 중 컴파일 시점 또는 실행 중
발생 원인 프로그래머 실수 외부 요인, 예상치 못한 상황
예시 NullPointerException, ArrayIndexOutOfBoundsException IOException, SQLException

왜 이런 구분이 필요할까요?

RuntimeException은 주로 프로그래머의 실수로 인해 발생하기 때문에, 모든 RuntimeException에 대해 예외 처리 코드를 작성하는 것은 번거롭고 코드의 가독성을 떨어뜨릴 수 있습니다. 따라서 컴파일러는 RuntimeException에 대한 예외 처리를 강제하지 않습니다.

반면에 Checked Exception은 외부 요인이나 예상치 못한 상황으로 인해 발생하기 때문에, 예외 처리를 하지 않으면 프로그램이 비정상적으로 종료될 수 있습니다. 따라서 컴파일러는 Checked Exception에 대한 예외 처리를 강제하여 프로그램의 안정성을 높입니다.

참고:

  • ErrorException과 다릅니다. Error는 시스템에 심각한 문제가 발생했을 때 발생하며, 일반적으로 복구가 불가능합니다.

Java 커스텀한 예외 만드는 방법

Java에서 기본적으로 제공되는 예외 클래스 외에도, 특정 상황에 맞는 커스텀 예외를 직접 만들어 사용할 수 있습니다. 커스텀 예외를 사용하면 코드의 가독성을 높이고 예외 상황을 더 명확하게 처리할 수 있습니다.

커스텀 예외 만드는 방법

  1. Exception 클래스 또는 RuntimeException 클래스 상속:
    • Checked Exception을 만들려면 Exception 클래스를 상속합니다.
    • Unchecked Exception을 만들려면 RuntimeException 클래스를 상속합니다.
  2. 생성자 정의:
    • 예외 발생 시 전달할 메시지를 받는 생성자를 정의합니다.
    • 일반적으로 두 개의 생성자를 정의합니다.
      • 매개변수가 없는 기본 생성자
      • 예외 메시지를 받는 생성자 (String message)
  3. 필요에 따라 추가적인 필드 및 메서드 정의:
    • 예외 상황에 대한 추가 정보를 저장하기 위한 필드를 추가할 수 있습니다.
    • 예외 정보를 가져오는 메서드를 추가할 수 있습니다.

예시

Java

// Checked Exception
public class InvalidInputException extends Exception {
    public InvalidInputException() {
        super();
    }

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

// Unchecked Exception
public class InsufficientBalanceException extends RuntimeException {
    public InsufficientBalanceException() {
        super();
    }

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

커스텀 예외 사용 예시

Java

public class Account {
    private double balance;

    public void withdraw(double amount) throws InsufficientBalanceException {
        if (amount > balance) {
            throw new InsufficientBalanceException("잔액이 부족합니다.");
        }
        balance -= amount;
    }
}

커스텀 예외를 사용하는 이유

  • 특정 예외 상황을 명확하게 표현: 기본 예외 클래스로는 표현하기 어려운 특정 예외 상황을 명확하게 나타낼 수 있습니다.
  • 추가적인 정보 제공: 예외 발생 원인에 대한 추가적인 정보를 담을 수 있습니다.
  • 코드 가독성 향상: 커스텀 예외를 사용하면 코드의 의도를 더 명확하게 파악할 수 있습니다.
  • 예외 처리 로직 분리: 특정 예외 상황에 대한 처리 로직을 분리하여 코드의 재사용성을 높일 수 있습니다.

참고

  • 커스텀 예외 클래스 이름은 일반적으로 Exception으로 끝납니다.
  • 예외 메시지는 가능한 한 상세하게 작성하여 디버깅에 도움이 되도록 합니다.

'코딩스터디 > JAVA스터디' 카테고리의 다른 글

Java) Enum  (3) 2024.11.17
Java 멀티쓰레드 프로그래밍  (2) 2024.11.16
Java) 인터페이스 (interface)  (6) 2024.11.14
Java 패키지  (6) 2024.11.13
Java) 상속_객체지향 프로그래밍(OOP)  (6) 2024.11.12