예외처리란
예외처리란 프로그램 실행 중 예기치 못한 상황이 발생했을 때 적절하게 조치를 취함으로써 프로그램이 수행하는 동작을 이상없이 진행하도록 하는 것입니다.
우리가 개발한 프로그램이 사용자의 컴퓨터에서 실행되는 도중에는 오류가 발생해도 프로그램을 수정할 수 없기 때문에 프로그램 개발 시 미리 예외처리 코드를 작성하여 대비해야 합니다.
에러와 같이 치명적인 오류는 처리하고 싶어도 처리할 수 없는 경우가 거의 대부분이기 때문에 오류 처리가 무의미하지만 예외의 경우에는 적절한 처리를 해준다면 신뢰성 있는 프로그램을 만드는 데에 큰 도움을 줄 것입니다.


예외의 발생
자바에서 예외가 발생했을때 어떠한 처리도 해주지 않는다면 이후 코드는 실행되지 않은 상태에서 JVM의 예외처리기까지 예외가 전달되고 예외처리기는 해당 예외에 대한 오류 내용을 출력하게 됩니다.
아래의 코드를 보면 null값인 문자열 s 가 "hello" 라는 문자열인지 비교하게 되는데 null값을 참조하였기 때문에 NullPointerException 이 발생하게 됩니다.
이때 아무 처리도 해주지 않았기때문에 예외처리가 해당 예외를 잡아 오류 내용을 출력하게 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
package test;
 
public class ExceptionTest {
    public static void main(String[] args) {
        
        String s = null;
        
        boolean isHello = s.equals("hello");
        System.out.println("s는 hello인가? " + isHello);
    
    }
}
cs

 

1
2
Exception in thread "main" java.lang.NullPointerException
    at snippet.ExceptionTest.main(ExceptionTest.java:8)
cs

 

 

 

 

예외처리방법 try - catch - finally 문

자바에서는 예외가 발생할 경우 JVM에서는 발생 즉시 프로그램의 진행을 멈추고 해당 예외의 내용을 객체로 만들어 던지게(throw) 되며, 개발자는 던져진 예외를 try - catch 구문을 통해 잡아서 처리할 수 있습니다.
예외가 발생할 가능성이 있는 코드를 try{ } 구문으로 감싸주고 try{ } 구문에서 예외가 발생하여 던져진 예외객체를 catch{ } 구문을 통해 잡아서 처리합니다.

finally{ } 의 경우 생략 가능하며, 예외 발생 여부와 상관없이 무조건 실행됩니다.
입출력 처리시 입출력 자원을 사용 후 반납해주는 경우처럼 예외 여부와 관계 없이 최종적으로 꼭 실행되어야 하는 로직이 들어가게 됩니다.

 

예외가 발생하지 않은 경우
try - catch - finally 구문으로 예외를 대비한 후 예외가 발생하지 않은 경우에는 다음과 같은 흐름으로 진행됩니다.

1. 코드1을 실행합니다.
2. 코드2를 실행합니다.
3. 코드3을 실행합니다.
4. finally 구문의 코드들을 실행합니다.
5. 이후 try - catch - finally 구문을 빠져나와 프로그램 정상 흐름대로 진행됩다.

예외가 발생한 경우
예외가 발생한 경우에는 흐름이 바뀌게 됩니다. 여기서는 코드2를 실행하다가 예외가 발생한것으로 가정하겠습니다.

1. 코드1을 실행합니다.
2. 코드2를 실행하다가 예외가 발생한다. 이때 코드3은 실행되지 않고 JVM은 알맞은 예외 객체를 만들어 던집니다.
3. catch 구문에서 던져진 예외를 잡아서 처리합니다.
   이때 위 그림의 4번에서 예외클래스는 JVM이 던진 예외객체의 클래스이거나 상위클래스여야만 잡을 수 있습니다.
   만약 알맞은 예외를 catch문에서 잡지 못한 경우에는 finally 구문 실행 이후 프로그램은 비정상 종료됩니다.
4. finally 구문의 코드들을 실행합니다.
5. 이후 try - catch - finally 구문을 빠져나와 프로그램 정상 흐름대로 진행됩다.


try - catch - finally 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String s = null;
 
try{
    
    s = s.toUpperCase(); //s를 대문자로 바꾸면서 NullPointerException 발생
    System.out.println(s); //실행되지 않는다.
    
}catch(NullPointerException e){
    
    s = "empty";
    System.out.println(s.toUpperCase()); //catch문에서 대문자로 바꾸어준다.
    
}finally{
    System.out.println("무조건 실행됩니다.");
}
 
cs

 

1
2
EMPTY
무조건 실행됩니다.
cs

 

위 코드를 보면 try - catch - finally 구문을 쉽게 이해할 수 있습니다.
try에서 String s를 toUpperCase()를 통해 대문자로 바꾸어 주는 과정에서 s에는 null값이 초기화 되어 있었기 때문에 NullPointerException이 발생합니다.
바로 아래 출력문은 실행되지 않고 catch문에서 NullPointerException 을 잡은 후 대문자 처리해줍니다.
이후 finally 구문이 실행되며 이후 프로그램이 정상적으로 진행됩니다.


예외 정보 출력하기

 

 

 

예외가 발생한 후 예외는 catch블록으로 던져진다고 했습니다.
정확히 말하면 예외 객체의 참조값(주소값)이 넘겨지는 것입니다.
해당 참조값은 catch 안의 참조변수에 저장되고 해당 객체는 예외에 대한 모든 정보들이 담겨있습니다.
이 정보들은 정확히 프로그램상의 어느위치에서 왜 발생했는지와 자세한 사항들에 대한것들을 포함합니다. 
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ExceptionTest {
    public static void main(String[] args) {
        
        try{
            int i = 3 / 0;
        }catch (ArithmeticException e) {
            // 예외가 어느 메서드 실행시에 발생한 것인지 스택 정보를 출력
            // e.printStackTrace();
            System.out.println(e.getClass() + e.getMessage());
        }
        
    }
}
cs

 

1
class java.lang.ArithmeticException/ by zero
cs

 

 

 

예외에 따라 catch 하기
지금까지는 catch 블록을 하나만 사용했지만 try 블록 안에서 하나의 예외만 발생할 것이라는 보장은 없습니다.
그렇기 때문에 발생한 예외에 따라 다른 방법으로 처리할 수 있는 방법이 필요합니다.
다음과 같이 여러개의 catch 블록을 통해 각 예외에 맞는 처리를 할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ExceptionTest {
    public static void main(String[] args) {
        
        try{
            //예외발생
        }catch (NullPointerException e) {
            // NullPointerException 혹은 하위 예외 발생시 처리
        }catch (ArithmeticException e) {
            // ArithmeticException 혹은 하위 예외 발생시 처리
        }catch (Exception e) {
            // Exception의 하위 예외 발생시 처리
        }
        
    }
}
cs

 

 

try 블록에서 예외가 발생하면 먼저 발생한 예외가 첫번째 catch 블록에 해당하는지 검사합니다.
catch 블록이 발생한 예외에 해당하면 예외를 객체로 만들고 던진 후 이후 catch 블록은 실행하지 않습니다.
이때 첫번째 catch 블록이 NullPointerException 이라면 상속 관계상 NullPointerException이거나 NullPointerException를 상속한 하위 예외여야 합니다.

첫번째 catch블록에서 처리하지 못한 경우에는 두번째 catch 블록도 마찬가지로 검사한 후 처리하고 처리하지 못한 경우 결국 마지막 catch 블록까지 전달됩니다.
여기서 마지막 catch 블록은 예외의 최상위 클래스인 Exception 이기 때문에 어떠한 예외가 발생하더라도 결국 마지막 catch 블록에서 처리할 수 있습니다.

 

 

 

 

참고글

[JAVA] 자바에서 예외란?

블로그 이미지

도로락

IT, 프로그래밍, 컴퓨터 활용 정보 등을 위한 블로그

,