보리차
chapter 18 예외처리(Exception Handling) 본문
자바 예외처리의 기본
자바에서 말하는 예외
프로그램 실행 중에 발생하는 '예외적인 상황'을 줄여서 '예외'라 한다. 즉 예외는 단순한 문법 오류가 아닌 실행 중간에 발생하는 '정상적이지 않은 상황'을 뜻한다.
예외 처리를 위한 try~catch
자바는 예외 상황별로 그 상황을 알리기 위한 클래스를 정의하고 있다. 이러한 클래스를 '예외 클래스'라 한다. 어떤 오류가 발생하면 가상머신은 해당 오류의 예외 클래스의 인스턴스를 생성한다. 이 인스턴스를 프로그래머가 처리하면 예외는 처리된 것으로 간주하여 프로그램을 종요하지 않지만 처리하지 않으면 프로그램은 그냥 종료가 된다.
예외를 처리할 때는 try~catch문을 사용한다. try 영역에서 발생한 예외 상황을 catch 영역에서 처리한다.
try {
...관찰 영역...
}
catch(Exception name) {
...처리 영역...
}
둘 이상의 예외를 처리하기 위한 구성
catch 구문 둘을 이어서 구성하면 된다.
try {
...
}
catch(Exception1 e) {
...
}
catch(Exception2 e) {
...
}
자바 7부터는 다음과 같이 하나의 catch 구문 안에서 둘 이상의 예외를 처리하는 것도 가능하다.
try {
...
}
catch(Exception1 | Exception2 e) {
...
}
Throwable 클래스
자바의 최상위 클래스인 java.lang.Object를 제외하고 예외 클래스의 최상위 클래스는 java.lang.Throwable 이다.
그리고 이 클래스에서 발생한 예외의 정보를 알 수 있는 메소드가 정의되어 있는데, 대표적인 메소드 둘은 다음과 같다.
- public String getMessage() : 예외의 원인을 담고 있는 문자열을 반환
- public void printStackTrace() : 예외가 발생한 위치화 호출된 메소드의 정보를 출력
class ExceptionMessage {
public static void md1(int n) {
md2(n, 0); // 이 지점으로 md2로부터 예외가 넘어온다.
}
public static void md2(int n1, int n2) {
int r = n1 / n2; // 이 지점에서 예외가 발생
}
public static void main(String[] args) {
try {
md1(3); // 이 지점에서 md1으로부터 예외가 넘어온다.
}
catch(ArithmeticException | InputMismatchException e) {
e.getMessage();
e.printStackTrace();
}
System.out.println("Good bye~~!");
}
}
출력 결과
Exception in thread "main" java.lang.ArithmeticException: / by zero
// 0으로 나누어 ArithmeticException 예외발생
at ExceptionMessage.md2(ExceptionMessage.java:6)
// ExceptionMessage 클래스의 md2에서 예외가 시작되어
at ExceptionMessage.md1(ExceptionMessage.java:3)
// md1으로 예외가 넘어갔으며
at ExceptionMessage.main(ExceptionMessage.java:9)
// main으로까지 예외가 넘어감
Good bye~~!
잘못된 catch 구문의 구성
class FirstException extends Exception {...}
class SecondException extends FirstException {...}
class ThirdException extends SecondException {...}
try {
....
}
catch(FirstException e) {...}
catch(SecondException e) {...}
catch(ThirdException e) {...}
예외처리만 보면 문제가 없을 듯한데 컴파일 오류가 발생한다. 그 내용은 다음과 같다.
"두번째, 세번째 catch 구문은 실행될 일이 절대 없습니다."
바로 SecondException과 ThirdException이 FirstException 을 직접 혹은 간접적으로 상속하고 있기 때문이다. 따라서 위와 같이 catch 구문을 구성하고자 한다면 다음과 같이 순서를 수정해야 한다.
try {
....
}
catch(ThirdException e) {...}
catch(SecondException e) {...}
catch(FirstException e) {...}
try ~ finally 구문
try {...
}
finally {... // try 안으로 진입하면, 무조건 실행
}
- try에서 예외 발생 유무, catch실행 유무에 상관없이 무조건 실행된다.
다음과 같이 try에서 예외가 발생하건 안하건, 반드시 처리해야하는(writer.close() ) 내용을 finally 구분에 둠으로써 close 메소드 호출을 보장할 수 있다. 그러나 close에서도 예외가 발생할 수 있기 때문에 코드를 다음과 같이 구성해야 한다.
try {
writer = Files.newBufferedWriter(file); // IOException 발생 가능
writer.write('A');
writer.write('Z');
}
catch(IOException e) {
e.printStackTrace();
}
finally {
try {
if(writer != null)
writer.close();
}
catch(IOException e) {
e.printStackTrace();
}
}
이렇듯 finally 내에서도 try~catch문을 작성할 수 있으나 코드가 복잡해질 수 있다. 그러나 자바 7에서 try-with-resources 문이 등장해 이 문장의 구성이 한결 단순해졌다.
try-with-resources 구문
try(BufferedWriter writer = Files.newBufferedWriter(file)) {
writer.write('A');
writer.write('Z');
}
catch(IOException e) {
e.printStackTrace();
}
참조변수 writer가 참조하는 인스턴스의 종료는 신경 쓰지 않아도 된다. try에서 예외가 발생하건 안 하건 writer.close()의 호출은 보장되기 때문이다. 그렇다면 리소스의 종료 관련 메소드가 close인 경우에만 자동으로 호출이 될까? 이에 대한 답을 다음 인터페이스에서 찾을 수 있다.
java.lang.AutoCloseable
이 인터페이스는 try-with-resources문에 의해 자동으로 종료되어야 할 리소스 관련 클래스가 반드시 구현해야 하는 인터페이스다. 그리고 이 인터페이스에는 void close() throws Exception 추상 메소드가 존재한다.
즉 try-with-resources문에서 호출하는 메소드는 AutoCloseable 인터페이스의 close 메소드이다. 따라서 close 이외의 메소드 호출은 기대할 수 없다.
예외처리는 성능의 저하로 이어진다.
try 구문 안에 위치한 코드는 실행 속도가 느리다. 따라서 과도한 예외처리는 심각한 성능의 저하로 이어질 수 있다. 규모가 클수록, 성능이 중요시될수록 try ~ catch문 이외의 다양한 방법으로 그리고 선별적으로 예외를 처리한다.
'Java' 카테고리의 다른 글
chapter 20 자바의 기본 클래스 (0) | 2022.02.10 |
---|---|
chapter 19 자바의 메모리 모델과 Object 클래스 (0) | 2022.02.08 |
chapter 17 인터페이스와 추상 클래스 (0) | 2022.02.06 |
chapter 16 클래스의 상속 3: 상속의 목적 (0) | 2022.02.06 |
chapter 15 클래스의 상속 2: 오버라이딩 (0) | 2022.01.28 |