Exception은 프로그램 실행 흐름을 방해하는 사건을 말합니다. 예외가 발생하면 프로그램이 비정상적으로 종료될 수 있으며, Java의 예외 처리 메커니즘을 통해 이러한 오류를 관리하여 프로그램이 복구하거나 정상적으로 종료되도록 합니다.
Class Throwable
Java에서 Exception은 Throwable 클래스를 상속받는 객체로 표현됩니다.
Throwable 클래스는 두 가지 주요 하위 클래스인 Error와 Exception을 가집니다.
Throwable
/ \
Error Exception
/ \
(UncheckedException) CheckedExceptions
RuntimeException
ThrowableThrowable 클래스는 모든 예외와 오류의 최상위 클래스입니다.Error와 Exception 클래스를 포함합니다.ErrorOutOfMemoryError, StackOverflowError 등이 있으며, 이러한 오류는 주로 JVM에서 발생하며 예외처리를 통해 애플리케이션 레벨에서 이를 정상적인 상태로 되돌리거나 문제를 해결하는 것이 거의 불가능합니다.ExceptionException 클래스는 크게 Checked Exception과 Unchecked Exception으로 나뉩니다.Checked Exception
IOException, SQLException, ClassNotFoundException 등이 여기에 속하며, 파일 입출력, 네트워크 작업, 데이터베이스 연결, 네트워크 통신 등에서 발생할 수 있는 예외입니다.try-catch 블록이나, throws 키워드를 사용하여 예외를 처리하도록 명시해야 합니다.//file 찾을때 없으면 예외 던지기 때문에 예외 처리
try {
FileReader reader = new FileReader("file.txt");
} catch (IOException e) {
e.printStackTrace();
}
Unchecked Exception (= RuntimeException 클래스와 그 하위 클래스)
RuntimeException 클래스와 그 하위 클래스들이 이에 속합니다. 예를 들면 NullPointerException, ArrayIndexOutOfBoundsException, ,IllegalArgumentException 등이 있습니다.public void divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
System.out.println(a / b);
}
요약
| 특성 | CheckedException | UncheckedException |
|---|---|---|
| 발생 시점 | 컴파일 시점 | 런타임 시점 |
| 강제 처리 여부 | Y, 반드시 처리해야 함 | N, 처리하지 않아도 됨 |
| 상속 구조 | Exception 클래스에서 파생 (단, RuntimeException 제외) | RuntimeException 클래스와 그 하위 클래스 |
| 주로 발생하는 상황 | 외부 자원 접근, 파일 입출력, 데이터베이스 등 | 프로그래머 실수, 논리 오류 등 |
| 예시 | IOException, SQLException 등 | NullPointerException, ArithmeticException 등 |
Java는 예외를 처리하기 위해 try, catch, finally, throw, throws와 같은 키워드를 제공합니다.
try : 예외가 발생할 수 있는 코드를 감쌉니다.catch : 예외가 발생했을 때 이를 처리하는 코드를 작성합니다.finally : 예외 발생 여부와 상관없이 항상 실행되는 블록으로, 주로 파일이나 데이터베이스 연결 같은 자원을 해제할 때 사용됩니다.throw : 명시적으로 예외를 발생시킬 때 사용합니다.throws : 메서드 선언부에서 해당 메서드가 특정 예외를 던질 수 있음을 명시합니다. 이를 통해 호출하는 쪽에서 예외를 처리할 수 있도록 합니다.try-catch-finally 예시 = 예외를 직접 처리하기
예외가 발생한 메서드 내에서 try-catch 블록을 사용하여 예외를 처리하는 방식입니다. 예외가 발생할 가능성이 있는 코드를 try 블록으로 감싸고, 발생한 예외를 catch 블록에서 처리합니다. 이 방법은 예외가 발생한 시점에서 문제를 바로 해결할 수 있게 해 줍니다.
public void readFile(String fileName) throws IOException {
try {
FileReader reader = new FileReader(fileName);
// 파일 읽기 작업 수행하..겠지?
} catch (IOException e) {
// 파일 열려고 했다가 실패했다는 것을 상위 메서드에게 알리기(전파)
throw e;
} finally {
// 자원 정리
if (reader != null) {
try {
reader.close();
} catch (IOException closeException) {
//원래 예외와 부딪힐 수 있기 때문에 로그만 남기기
System.err.println("파일을 닫는 중 오류가 발생했습니다: " + closeException.getMessage());
}
}
}
}
throw 예시 = 예외를 명시적으로 던지기
프로그램에서 특정 상황을 만나면 예외를 명시적으로 던지는 방법입니다. 주로 메서드 내부에서 잘못된 조건이 발생했을 때, throw 키워드를 사용하여 예외를 발생시킵니다.
예외가 RuntimeException 계열이든 Checked Exception이든 상관없이, throw를 통해 던질 수 있습니다.
하지만 Checked Exception을 던질 경우, 해당 예외가 발생할 수 있음을 메서드 선언부에 throws 키워드를 통해 명시해야 합니다. 그렇지 않으면 컴파일 오류가 발생합니다.
public void validateAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("나이는 음수일 수 없습니다.");
}
}
throws 예시 = 예외를 호출한 메서드로 던지기
메서드에서 발생한 예외를 자신이 처리하지 않고 상위 메서드로 던지는 방식입니다. 메서드 선언부에 throws 키워드를 사용하여 호출한 메서드가 예외를 처리하도록 합니다. 주로 Checked Exception을 처리할 때 사용됩니다.
public void connectToDatabase() throws SQLException {
try {
// 데이터베이스에 연결 시도
connection = DriverManager.getConnection(url, username, password);
System.out.println("데이터베이스에 성공적으로 연결되었습니다.");
// 데이터베이스 작업 수행 (여기에서는 생략)
} catch (SQLException e) {
System.err.println("데이터베이스 연결 중 오류가 발생했습니다: " + e.getMessage());
throw e; // 예외를 다시 던짐
}
}
정리
Catch: 예외를 직접 처리하는 방식
Throws: 예외를 상위 메서드로 던지는 방식
Throw: 조건에 따라 명시적으로 예외를 발생시키는 방식
Java에서 예외 처리는 성능에 영향을 미칠 수 있으며 일반적인 프로그램보단 예외가 자주 발생하거나 성능이 중요한 애플리케이션(실시간 혹은 고성능)에서는 주의해야합니다.
예외를 예외적인 상황에서만 사용하기:
입력 값 null 검증을 통해 예외 발생을 방지하거나, 배열의 인덱스가 범위를 벗어났는지 검사할 때는 조건문을 사용해 검사하는 것이 좋습니다.// ppp 비효율적인 코드 (예외를 정상적인 흐름에 사용)
try {
int value = array[index];
} catch (ArrayIndexOutOfBoundsException e) {
// 예외 처리 코드
}
// bbb 효율적인 코드 (조건문 사용)
if (index >= 0 && index < array.length) {
int value = array[index];
} else {
// 오류 처리 코드
}
예외를 캐치한 후 불필요한 작업을 줄이기:
예외가 자주 발생하는 코드의 구조를 최적화하기:
- 예외가 자주 발생하는 구간이 있다면 해당 구간의 로직을 재설계하거나, 예외 발생을 최소화할 수 있는 다른 방법을 모색해야 합니다.
필요한 경우에는 커스텀 예외를 사용해 명확성/유지보수성 향상:
- RuntimeException이나 다른 표준 예외를 사용하는 대신, 상황에 맞는 커스텀 예외를 정의해 사용함으로써 불필요한 예외 발생을 줄일 수 있습니다. 특히, 예외 메시지나 로깅을 통해 문제의 원인을 쉽게 파악할 수 있도록 하는 것이 중요합니다.