Java에서 Runnable의 run 메서드 내 IOException 처리 방법
문제 상황
Runnable 인터페이스의 run 메서드는 예외를 던질 수 없기 때문에, IOException과 같은 체크 예외를 직접 던지려고 하면 컴파일 오류가 발생한다.
try {
// 예외가 발생할 수 있는 코드
} catch (IOException ioe) {
throw ioe; // 컴파일 오류 발생
}
위 코드에서는 IOException을 다시 던지려고 시도하지만, run 메서드는 체크 예외를 던질 수 없으므로 컴파일러가 이를 허용하지 않는다. 이로 인해 unreported exception IOException; must be caught or declared to be thrown 라는 컴파일 오류가 발생한다.
컴파일 오류가 발생하는 이유?
- 체크 예외: Java에서 IOException은 체크 예외로, 반드시 처리하거나 메서드 선언에 throws 키워드로 명시해야 한다.
- run 메서드는 Runnable 인터페이스의 메서드로, 예외를 던질 수 있도록 설계되지 않았다.
해결 방법
- 예외를 try-catch 블록에서 처리
- 체크 예외를 RuntimeException으로 변환
1. try-catch 블록에서 예외 처리
run 메서드 내에서 발생할 수 있는 예외를 try-catch 블록으로 처리하는 방법입니다.
public class IOExceptionHandlingInRunnable {
public static void main(String[] args) {
Runnable task = new Runnable() {
@Override
public void run() {
try {
simulateIOException();
} catch (IOException ioe) {
System.err.println("IOException 처리: " + ioe.getMessage());
}
}
private void simulateIOException() throws IOException {
throw new IOException("예제 IOException 발생");
}
};
Thread thread = new Thread(task);
thread.start();
}
}
- 장점: 예외를 run 메서드 내에서 즉시 처리
- 단점: 호출자에게 예외 정보를 전달하지 않기 때문에, 예외 발생을 외부에서 알 수 없다
2. 체크 예외를 RuntimeException으로 변환
체크 예외인 IOException을 언체크 예외인 RuntimeException으로 변환하여 다시 던집니다.
public class IOExceptionHandlingInRunnable {
public static void main(String[] args) {
Runnable task = new Runnable() {
@Override
public void run() {
try {
simulateIOException();
} catch (IOException ioe) {
throw new RuntimeException("IOException 발생", ioe);
}
}
private void simulateIOException() throws IOException {
throw new IOException("예제 IOException 발생");
}
};
Thread thread = new Thread(task);
thread.start();
}
}
- 장점: RuntimeException은 언체크 예외이므로, run 메서드에서 자유롭게 던질 수 있다.
- 단점: 예외를 감싸서 던지기 때문에 호출자가 예외를 받을 때 예외 체인을 통해 원래의 예외를 분석해야 한다.
simulateIOException 메서드
이 메서드는 IOException을 강제로 발생시키는 예제.
private void simulateIOException() throws IOException {
throw new IOException("예제 IOException 발생");
}
RuntimeException에 대한 추가 설명
RuntimeException은 Java의 표준 예외 계층에서 언체크 예외(Unchecked Exception)의 기본 클래스이다.
이는 java.lang 패키지에 속하며, 개발자가 명시적으로 예외 처리를 강제하지 않아도 된다.
주요 특징
- 언체크 예외: RuntimeException을 상속한 예외는 컴파일러가 예외 처리를 강제하지 않는다. 즉, try-catch 블록이나 throws 선언 없이도 사용할 수 있다.
- 상속 구조: RuntimeException은 Exception 클래스를 상속받지만, 체크 예외가 아닌 언체크 예외로 분류된다.
public class RuntimeException extends Exception {
// ...
}
사용 목적
- 프로그래밍 오류: 런타임 예외는 보통 프로그래밍 오류, 잘못된 API 사용, 논리적 버그 등을 나타내기 위해 사용된다.
- 예: NullPointerException, IndexOutOfBoundsException
- 예외 처리의 단순화: 언체크 예외를 통해 예외 처리 코드를 간결하게 유지할 수 있다.
예제
public class RuntimeExceptionExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
System.out.println(numbers[3]); // ArrayIndexOutOfBoundsException 발생
}
}
- 위 코드에서 ArrayIndexOutOfBoundsException은 RuntimeException의 서브클래스이며, 예외 처리를 강제하지 않습니다.
주의사항
- 적절한 사용: 언체크 예외를 남용하면 예외 처리 로직이 누락되어, 예상치 못한 프로그램 종료로 이어질 수 있다.
- 문서화: 메서드가 RuntimeException을 던질 수 있음을 명확히 문서화하여 사용자에게 예외 발생 가능성을 알리는 것이 중요합니다.
체크 예외(Checked Exception)와 언체크 예외(Unchecked Exception)
Java에서 예외(Exception)는 크게 체크 예외(Checked Exception)와 언체크 예외(Unchecked Exception)로 나눌 수 있다.
1. 체크 예외 (Checked Exception)
정의
체크 예외는 컴파일 타임에 예외 처리를 강제하는 예외이다.
즉, 해당 예외가 발생할 수 있는 코드를 작성하면 반드시 예외 처리를 해주어야 한다.
예외가 발생할 가능성이 있는 메서드는 throws 키워드를 사용해 해당 예외를 던질 수 있음을 선언하거나, try-catch 블록을 사용하여 예외를 처리해야 한다.
특징
- 컴파일 타임에 검사 됨: 체크 예외는 반드시 처리해야 하므로, 예외가 발생할 수 있는 코드에서 컴파일 오류를 방지하기 위해 예외 처리를 강제로 하도록 요구한다.
- 명시적인 예외 처리: 예외를 명시적으로 처리하거나 메서드 서명에 throws를 추가하여 호출자에게 처리 책임을 위임해야 한다.
예시
체크 예외는 일반적으로 입출력(I/O), 네트워크 등 외부 리소스를 사용할 때 발생할 수 있다.
import java.io.*;
public class CheckedExceptionExample {
public static void main(String[] args) {
try {
readFile("example.txt");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readFile(String filename) throws IOException {
FileReader reader = new FileReader(filename);
BufferedReader br = new BufferedReader(reader);
br.readLine();
}
}
- 위 코드에서 FileReader와 BufferedReader는 IOException을 발생시킬 수 있으므로, readFile 메서드는 throws IOException을 선언하여 예외 처리를 강제
- 호출자는 try-catch를 사용해 예외를 처리하거나, 예외를 다시 던질 수 있다.
대표적인 체크 예외
- IOException
- SQLException
- FileNotFoundException
- ParseException
2. 언체크 예외 (Unchecked Exception)
정의
언체크 예외는 컴파일 타임에 예외 처리를 강제하지 않는 예외이다.
이러한 예외는 try-catch 블록을 사용하지 않아도 컴파일 오류가 발생하지 않으며, 예외 처리가 선택적이다.
주로 런타임 중 발생할 수 있는 예외로, 프로그래밍 오류나 논리적 실수에서 발생한다.
특징
- 컴파일 타임 검사 없음: 언체크 예외는 컴파일러가 예외 처리를 강제하지 않으므로, 예외를 처리하지 않고도 프로그램을 실행할 수 있다.
- 주로 프로그래밍 오류: 잘못된 API 사용이나 논리적 오류로 발생하며, 개발자가 미리 예측하고 방지해야 할 상황에서 발생한다.
예시
언체크 예외는 NullPointerException, ArrayIndexOutOfBoundsException 등과 같이 자주 발생하는 예외
public class UncheckedExceptionExample {
public static void main(String[] args) {
String text = null;
System.out.println(text.length()); // NullPointerException 발생
}
}
- 위 예시에서 NullPointerException은 언체크 예외이다. 예외를 처리하지 않으면 런타임에서 예외가 발생하지만, 컴파일 타임에 예외 처리를 강제하지 않는다
대표적인 언체크 예외
- NullPointerException
- ArrayIndexOutOfBoundsException
- ArithmeticException
- IllegalArgumentException
- IllegalStateException
체크 예외 vs 언체크 예외
컴파일 타임 검사 | 컴파일러가 예외 처리를 강제 | 컴파일러가 예외 처리를 강제하지 않음 |
처리 방식 | 반드시 try-catch 블록을 사용하거나 throws 선언 | 예외 처리 선택적 (필수 아님) |
주로 발생하는 상황 | I/O 작업, 네트워크 작업, 파일 처리 등 외부 리소스 사용 시 | 논리적 오류, 잘못된 코드 사용 시 |
대표적인 예외 | IOException, SQLException, FileNotFoundException | NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException |
언제 체크 예외를 사용하고 언제 언체크 예외를 사용해야 할까?
- 체크 예외 사용:
- 예측 가능한 외부 리소스 문제: 예를 들어 파일을 읽을 때 파일이 없거나 네트워크 연결이 끊어지는 상황처럼, 외부 환경에서 발생할 수 있는 문제는 체크 예외로 처리하는 것이 적합하다
- 예외가 반드시 처리되어야 하는 경우: 예외가 발생하면 시스템 안정성에 심각한 영향을 미칠 수 있어 반드시 처리해야 할 때 체크 예외를 사용한다
- 언체크 예외 사용:
- 개발자 실수: 예를 들어, NullPointerException이나 ArrayIndexOutOfBoundsException과 같은 예외는 프로그래밍 오류로 발생하며, 개발자가 코드 로직을 수정하여 방지해야한다.
- 자주 발생하는 예외: 언체크 예외는 예상할 수 없는 오류가 발생할 수 있는 예외로, 예외 처리보다는 오류를 방지하는 방식으로 작성하는 것이 좋다.
'Study > Java' 카테고리의 다른 글
[프로그래머스(Java)] 문자열 밀기 / substring(). repeat() (0) | 2023.10.08 |
---|