1. 프로그램 오류
프로그램은 어떤 원인에 의해 오작동 또는 비정상적 종료가 일어나는 경우가 있다.
이러한 결과를 초래하는 원인을 프로그램 오류 또는 에러라고하며 발생 시점에 따라 3가지로 나뉜다.
- 컴파일 에러 : 컴파일 시에 발생하는 오류
- 런타임 에러 : 실행 시 발생하는 오류
- 논리적 에러 : 실행은 되지만, 의도와 다르게 동작하는 것
컴파일러는 소스코드(.java) 오타, 잘못된 구문, 자료형 체크 등 기본적인 검사를 수행하여 오류가 있는지 알려준다. 이때 발생하는 것이 컴파일 에러이다.
그리고 이 컴파일 과정이 끝나면 클래스 파일(.class)이 생성되고 생성된 클래스 파일을 실행할 수 있게 된다.
하지만, 컴파일이 잘되었어도 실행 도중 발생할 수 있는 잠재적 오류까지 검사할 수 없기 때문에 실행 도중 오류가 발생할 수 있다.
이때 발생하는 것이 런타임 에러이며 자바에서는 이를 에러(Error)와 예외(Exception)로 구분한다.
2. 에러 vs 예외
- 에러(Error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
- 예외(Exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
에러는 메모리 부족(OutOfMemoryError)나 스택오버플로우(StackOverflowError)와 같이 일단 발생하면 복구할 수 없는 심각한 오류이고,
예외는 NullPointerException, illegalArgumentException과 같이, 발생하더라도 적절한 코드(try-catch)를 미리 작성해 놓음으로써 프로그램의 비정상적 종료를 막을 수 있는 비교적 덜 심각한 오류이다.
3. 에러와 예외 클래스 계층 구조
오류(Error)와 예외(Exception) 모두 Throwable를 상속받는다.
모든 Error와 Exception 클래스는 Throwable 클래스를 상속받고있으며, getMessage()와 printStackTrace() 메서드를 통해 현재 Error와 Exception 여부를 확인할 수 있다.
사용자는 Error의 상황을 미리 미연에 방지하기 위해서 Exception 상황을 만들 수 있으며, java에서는 try-catch문으로 Exception handling을 할 수 있다.
Checked Exception, Unchecked Exception
Exception클래스는 두 그룹으로 나눌 수 있다.
- Exception클래스와 자손들 (RuntimeException클래스와 그 자손들 제외) ➡️ Checked Exception
- RuntimeException클래스와 자손들 ➡️ Unchecked Exception
쉽게 말하면, RuntimeException을 상속하지 않은 클래스는 Checked Exception,
반대로 상속한 클래스는 Unchecked Exception으로 분류할 수 있다.
Checked Exception
- 컴파일러가 예외처리를 강제(확인)하는 예외
- 사용자(외부)의 동작에 의해서 발생될 수 있는 예외
- ex) 존재하지 않는 파일의 입력(FileNotFoundException), 클래스 이름을 잘못 입력(ClassNotFoundException) 등
- 트랜잭션 도중 예외가 발생하면 롤백(rollback)하지 않음
명시적인 예외 처리를 강제하기 때문에 Checked Exception이라 한다. 반드시 try ~ catch로 예외를 잡거나 throw로 호출한 메소드에게 예외를 던져야 한다.
만일 컴파일 시점에 예외에 대해 처리(try/catch) 하지 않는다면 컴파일 에러가 발생하게 된다. 또한 트랜잭션 Rollback이 안된다는 특징도 있다.
Checked Exception 예시는 아래와 같다.
public sendFile(String fileName)() {
File file;
try {
file = FileFindService.find(fileName);
} catch (FileNotFoundException e){
// 기본 파일을 찾아서 전송한다.
file = FileFindService.find("default.png");
}
send(file);
}
}
Unchecked Exception
- 컴파일러가 예외처리를 강제(확인)하지 않는 예외
- ex) null인 참조변수의 멤버호출(NullPointException), 배열의 범위를 벗어난 동작(ArrayIndexOutOfBoundException) 등
- 트랜잭션 도중 예외가 발생하면 자동 롤백(rollback)
💡 중요 : 가장 큰 차이점은 예외에 대한 처리를 컴파일 시점에 강제하냐 안하냐의 차이입니다. (예외를 언제 던지냐의 차이가 아닙니다.) 코드에 Checked Exception에 대한 예외 처리가 안되어 있을 때 발생하는 건 컴파일 에러입니다.
Uncheckced Exception은 왜 예외처리를 강제하지 않을까?
RuntimeException클래스와 자손들은 예외처리 강제하지 않는다.
만일 RuntimeException클래스들에 속하는 예외에도 예외처리를 필수로해야한다면? 아래와 같이 참조변수와 배열이 사용되는 모든 곳에 예외처리를 해주어야할 것이다.
try {
int[] arr = new int[10];
System.out.println(arr[0]);
} catch(ArrayIndexOutOfBoundException ae) {
...
} catch(NullPointerException ne) {
...
}
이외에도 예외처리가 불필요한 경우 try-catch문을 무조건 넣어야 하므로 코드가 복잡해지게 된다.
왜 Checked Exception은 롤백해주지 않는걸까?
CheckedException은 예외처리가 컴파일러에 의해 강제되어있기 때문에 롤백 혹은 다른 처리를 개발자가 진행할 수 있는 기회가 있다. 하지만 Unchecked Exception은 예외 처리가 되어있지 않은 경우가 훨씬 많다. 이 경우 commit은 치명적일 수 있기때문에 롤백을 수행해준다라고 한다.
Exception Handling
Java에서 모든 예외가 발생하면 (XXX)Exception 객체를 생성한다.
예외를 처리하는 방법에는 크게 2가지가 있다.
1. try-catch
직접 try-catch를 이용해서 예외에 대한 최종적인 책임을 지고 처리하는 방식
try {
// 예외가 발생할 가능성이 있는 코드
} catch(Exception1 e1) {
// 예외1이 발생했을 경우, 처리하기 위한 코드
} catch(Exception2 e1) {
// 예외2이 발생했을 경우, 처리하기 위한 코드
} catch(Exception3 e1) {
// 예외3이 발생했을 경우, 처리하기 위한 코드
} finally {
// 예외 발생여부에 관계없이 항상 수행되어야하는 문장을 넣는다. (선택 사항)
}
2. 예외 처리 회피
throws Exception을 이용해서 발생한 예외의 책임을 호출하는 쪽이 책임지도록 하는 방식 (주로 호출하는 쪽에 예외를 보고할 때 사용함)
하지만, 무책임하게 상위 메서드에 throw로 예외를 던지는 것은 상위 메서드의 책임이 증가하기 때문에 좋지 않은 방법이다.
void method() throws Exception1, Exception2, Exception3 {
...
}
3. 예외 전환
예외 처리 회피와 비슷하게 메소드 밖으로 예외를 던지지만, 그냥 던지지 않고 적절한 예외로 전환해서 넘기는 방법이다. 명확한 의미로 전달되기 위해 적합한 의미를 가진 예외로 변경해야 한다.
public Object method() {
try {
...
} catch (IOException e) {
throw new CustomException ("IOException 발생");
}
}
public class CustomException extends RuntimeException{
public CustomException(String message) {
super(message);
}
}
참고자료
https://steady-coding.tistory.com/583
'Java' 카테고리의 다른 글
[Java] 제네릭(Generic) 이란? (0) | 2025.01.31 |
---|---|
[Java] 스레드 생명주기와 스케줄링 (0) | 2025.01.28 |
[Java] Weak Refrence란? (0) | 2024.12.17 |
[Java] static 블록과 생성자 (1) | 2024.12.14 |
[Java] 기본타입 vs 참조타입 (1) | 2024.06.10 |