JAVA

Java 언어의 예외 처리 (Exception Handling)

지오준 2024. 9. 15.
반응형

Java는 예외 처리(Exception Handling)를 통해 프로그램 실행 중 발생하는 오류를 관리할 수 있는 강력한 메커니즘을 제공합니다.

예외 처리는 프로그램의 비정상적인 종료를 방지하고, 오류 상황을 적절히 처리할 수 있도록 돕습니다.

이번 포스트에서는 Java의 예외 처리에 대해 자세히 설명하고, 샘플 코드를 통해 어떻게 활용되는지 알아보겠습니다.

 

1. 예외(Exception)란 무엇인가?

예외란 프로그램 실행 도중 예상치 못한 오류가 발생할 때 이를 처리하기 위한 메커니즘입니다.

예외가 발생하면 Java는 프로그램의 실행을 중지하고, 예외 객체를 생성하여 적절한 처리 코드를 찾습니다.

 

Java에서의 예외는 java.lang.Exception 클래스를 상속받아 정의되며, 크게 두 가지로 구분됩니다:

  • Checked Exception: 컴파일 시점에 예외 처리 여부를 확인해야 하는 예외입니다. 예외 처리를 강제합니다. 예를 들어, 파일을 열 때 발생할 수 있는 IOException이 여기에 해당합니다.
  • Unchecked Exception (RuntimeException): 런타임에 발생하는 예외로, 개발자가 명시적으로 예외 처리를 하지 않아도 됩니다. 주로 프로그래머의 실수나 논리적 오류로 인해 발생하는 NullPointerException이나 ArrayIndexOutOfBoundsException 등이 있습니다.

2. 예외 처리의 기본 구조

Java의 예외 처리는 주로 try-catch-finally 블록을 사용합니다. 기본적인 예외 처리 구조는 다음과 같습니다:

try {
    // 예외가 발생할 수 있는 코드
} catch (ExceptionType1 e1) {
    // ExceptionType1에 대한 처리
} catch (ExceptionType2 e2) {
    // ExceptionType2에 대한 처리
} finally {
    // 예외 발생 여부와 관계없이 항상 실행되는 코드
}
  • try 블록: 예외가 발생할 수 있는 코드를 감쌉니다.
  • catch 블록: 예외가 발생했을 때, 해당 예외를 처리하는 코드를 작성합니다.
  • finally 블록: 예외 발생 여부와 상관없이 항상 실행되는 코드입니다. 주로 자원 해제에 사용됩니다.

3. 예외 처리의 흐름

  • 예외 발생: try 블록 내에서 예외가 발생하면, 프로그램의 흐름은 즉시 catch 블록으로 넘어갑니다.
  • catch 블록: 예외가 발생하면 첫 번째로 해당 예외 타입과 일치하는 catch 블록을 찾습니다. 일치하는 블록이 있으면 그 블록의 코드를 실행합니다.
  • finally 블록: finally 블록은 예외 발생 여부와 상관없이 항상 실행됩니다. 자원 반납이나 정리 작업에 자주 사용됩니다.

4. 샘플 코드: 파일 읽기에서 발생할 수 있는 예외 처리

다음은 파일을 읽는 과정에서 발생할 수 있는 IOException과 FileNotFoundException을 처리하는 예제입니다.

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class ExceptionHandlingExample {

    public static void main(String[] args) {
        File file = new File("example.txt");
        FileReader fileReader = null;

        try {
            fileReader = new FileReader(file);
            int data;
            while ((data = fileReader.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error: File not found. Please check the file path.");
        } catch (IOException e) {
            System.out.println("Error: An IO error occurred.");
        } finally {
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                System.out.println("Error: Failed to close the file reader.");
            }
        }
    }
}

코드 설명

  • try 블록: 파일을 읽는 코드를 작성하였습니다. FileReader는 파일이 존재하지 않거나 읽는 도중 오류가 발생할 수 있으므로 예외가 발생할 수 있습니다.
  • catch 블록: FileNotFoundException이 발생하면 파일을 찾을 수 없다는 메시지를 출력하고, IOException이 발생하면 입출력 오류가 발생했다는 메시지를 출력합니다.
  • finally 블록: finally 블록에서는 파일을 다 사용한 후 반드시 닫아줘야 하는 작업을 처리합니다. 예외 발생 여부와 관계없이 fileReader를 닫는 코드를 실행합니다.

5. 사용자 정의 예외 (Custom Exception)

Java에서는 필요에 따라 사용자 정의 예외를 만들 수 있습니다.

예를 들어, 특정 비즈니스 로직에서 발생하는 예외를 처리하기 위해 새로운 예외 클래스를 정의할 수 있습니다.

사용자 정의 예외는 Exception 클래스를 상속받아 만들 수 있습니다.

class InvalidAgeException extends Exception {
    public InvalidAgeException(String message) {
        super(message);
    }
}

public class CustomExceptionExample {
    public static void checkAge(int age) throws InvalidAgeException {
        if (age < 18) {
            throw new InvalidAgeException("Age must be 18 or older.");
        } else {
            System.out.println("Welcome!");
        }
    }

    public static void main(String[] args) {
        try {
            checkAge(15);
        } catch (InvalidAgeException e) {
            System.out.println("Caught the exception: " + e.getMessage());
        }
    }
}

코드 설명

  • 사용자 정의 예외 InvalidAgeException: 나이가 18세 미만일 때 예외를 발생시키는 예제입니다. 예외가 발생하면 InvalidAgeException 클래스가 호출되어 메시지를 출력합니다.
  • throws: checkAge 메서드는 InvalidAgeException을 던질 수 있다고 명시되어 있습니다.

6. 예외 전파 (Exception Propagation)

예외는 메서드 호출 간에도 전파될 수 있습니다. 하나의 메서드에서 발생한 예외가 다른 메서드로 전달되어 처리될 수 있습니다.

예외 전파는 throws 키워드를 사용하여 메서드의 예외 발생 가능성을 선언하고, 상위 호출 메서드에서 처리할 수 있도록 합니다.

public class PropagationExample {
    public static void methodA() throws IOException {
        methodB();
    }

    public static void methodB() throws IOException {
        throw new IOException("Exception in methodB");
    }

    public static void main(String[] args) {
        try {
            methodA();
        } catch (IOException e) {
            System.out.println("Caught an exception: " + e.getMessage());
        }
    }
}

코드 설명

  • methodA와 methodB: methodB에서 발생한 IOException이 methodA로 전파되고, 결국 main 메서드에서 예외가 처리됩니다.
  • 예외 전파: 예외가 발생하면, 처리되지 않은 경우 상위 메서드로 전파됩니다.

7. 결론

Java의 예외 처리는 프로그램이 안정적으로 동작하도록 돕는 중요한 메커니즘입니다. 

try-catch-finally 구조를 통해 발생할 수 있는 오류를 처리하고, 필요하다면 사용자 정의 예외를 만들 수 있습니다.

예외 처리를 적절히 사용하면 프로그램이 예상치 못한 상황에서도 우아하게 대처할 수 있습니다.

이 포스트에서는 기본적인 예외 처리 구조와 예외 전파, 그리고 사용자 정의 예외에 대해 알아보았습니다.

예외 처리는 코드의 가독성을 높이고, 프로그램의 안정성을 향상시키는 중요한 기법입니다.

반응형

댓글