관리 메뉴

보리차

chapter 18 예외처리(Exception Handling) 본문

Java

chapter 18 예외처리(Exception Handling)

보리콩 2022. 2. 6. 22:31

자바 예외처리의 기본

자바에서 말하는 예외

프로그램 실행 중에 발생하는 '예외적인 상황'을 줄여서 '예외'라 한다.  즉 예외는 단순한 문법 오류가 아닌 실행 중간에 발생하는 '정상적이지 않은 상황'을 뜻한다.

 

예외 처리를 위한 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문 이외의 다양한 방법으로 그리고 선별적으로 예외를 처리한다.