본문으로 바로가기
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


자바의 예외 처리(Exception handling)


프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비상적으로 종료되는 것을 에러 또는 오류라고 하고 합니다.

이런 것들은 발생 시점에 따라 두 가지 종류로 나누어집니다.

1. 컴파일 에러(Compile-time Error) : 컴파일 할 때 발생하는 에러
2. 런타임 에러(Runtime Error) : 프로그램이 실행 도중에 발생하는 에러


자바에서는 실행 시 발생할 수 있는 프로그램 오류를 에러(Error)와 예외(Exception) 두 가지로 구분하였습니다.

1. 에러 : 프로그램이 실행하다가 발생하는 문제 중에 심각한 컴퓨터 관련 문제로 해결할 수 없는 것
2. 예외 : 심각한 문제가 아닌 문제 발생 상황, 에러에 비해 비교적 덜 심각한 상황


또한 예외(Exception)중에서도 프로그래머가 관리해야할 예외(Exception)과 실행중에 발생하는 예외를 RuntimeException으로 나눠놓았습니다.

 - Exception : 미리 예외상황을 어떻게 처리할 지 정해야한다.
 - RuntimeException : 실행 중 발생하는 예외이지만, 코딩할 때 처리할 필요는 없다.

출처 : http://www.w3ii.com/ko/java/java_exceptions.html


위에 사진은 간단하게 에러와 예외의 구조를 보여주기 위한 이미지이고 실제로 Exception의 종류는 셀 수 없이 많습니다.

모든 예외를 공부할 필요는 없고, 예외가 발생햇을 때 인터넷을 찾아보거나 이름을 보고 어떤 경우에 예외가 발생했는지 아는 것이 중요합니다.


예외 클래스는 Throwable을 상속받는데 이 클래스에서는 자주사용하는 메소드가 있습니다.

String getMessage() : 상세 오류 메시지를 제공
void printStackTrace() : 오류발생 시, 메소드 호출 경로가 저장된 스택의 내용을 출력


자바의 Exception Handling

자바는 프로그램을 실행중에 발생하는 예외(Exception)을 처리할 수 있는 예외 처리문을 제공하고 있습니다.

자바의 예외 처리는 4가지 방법으로 처리할 수 있습니다.

1. 예외가 발생한 메서드 안에서 처리(try-catch-finally)

2. 예외가 발생한 메서드를 호출한 곳에서 처리(throws)

3. 강제로 예외를 발생시켜서 처리(throw)

4. 사용자 예외를 생성하여 처리


1. 예외가 발생한 메서드 안에서 처리(try-catch-finally)

try { //실행 프로그램이 작성 //예외가 발생할 가능성이 있는 코드 } catch(예외타입1) { //try블록 안에서 예외 타입1이 발생할 경우 처리할 코드 } catch(예외타입2) { //try블록 안에서 예외 타입2가 발생할 경우 처리할 코드 } finally { //예외가 발생해도 실행하고, 예외가 발생하지 않아도 실행되는 코드 }


try : 예외가 발생할 가능성이 있는 범위를 지정하는 블록이다. try 블록은 최소한 하나의 catch 블록이 있어야 하며, catch 블록은 try 블록 다음에 위치한다. catch : 블록의 매개변수는 예외 객체가 발생했을 때 참조하는 변수명으로 반드시 java.lang.Throwable 클래스의 하위 클래스 타입으로 선언되어야 합니다. finally : 이 블록은 필수 변록이 아니며, finally 블록은 catch 유무와 상관 없이 무조건 수행된다. 주로 IO 또는 Connection을 close() 하는데 사용된다.


예제를 들어보면 아래 코드를 실행하면 결과가 0이 나와야 하겠지만 실행 결과는 

public class Test {
    public static void main(String[] args) {
        while(true) {
            Scanner scan = new Scanner(System.in);
            int value = scan.nextInt();
            System.out.println(9/value);
        }
    }
}


실행 결과

3
3
0
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Test.main(Test.java:8)


ArithmeticException이 발생하였다. 이 예외는 컴파일에서 발생하지 않고, 실행중(Runtime)중에 발생하였음을 알 수 있습니다.

또한 개발자가 예상치 못한 상황에서 Exception이 발생하여 처리를 강제로 시스템이 종료되는 것을 볼 수 있습니다.

위의 소스를 예외처리하여 사용하면 Exception이 발생하여도 프로그램이 실행되는 것을 알 수 있습니다.



public class Test {
    public static void main(String[] args) {
        while(true) {
            try {
                Scanner scan = new Scanner(System.in);
                int value = scan.nextInt();
                System.out.println(9/value);
            } catch(ArithmeticException e) {
                e.printStackTrace();
            }
        }
    }
}


실행 결과

9
1
3
3
0
java.lang.ArithmeticException: / by zero
    at Test.main(Test.java:9)

2
4


finally는 어떻게 해서 실행되는 것인지 아래 예제를 보면 알 수 있습니다.

Exception이 발생 유무 상관 없이 무조건 finally 블록 안에 코드가 실행되는 것을 알 수 있습니다.

public class Test {
    public static void main(String[] args) {
        while(true) {
            try {
                Scanner scan = new Scanner(System.in);
                int value = scan.nextInt();
                System.out.println(9/value);
            } catch(ArithmeticException e) {
                e.printStackTrace();
            } finally {
                System.out.println("무조건 실행됩니다.");
            }
        }
    }
}


실행 결과

9
1
무조건 실행됩니다.
0
java.lang.ArithmeticException: / by zero
무조건 실행됩니다.
    at Test.main(Test.java:9)
1
9
무조건 실행됩니다.


2. 예외가 발생한 메서드를 호출한 곳에서 처리(throws)

자바에는 예외가 발생한 지점이 아닌, 예외가 발생한 코드를 호출한 지점으로 예외를 전달하여 처리하는 방법이 있습니다.

자바에서는 이 처리 방법을 위해 throws를 사용합니다.

public class Test {
    public static void main(String[] args) {
        while(true) {
            try {
                divisionMethod();
            } catch(ArithmeticException e) {
                e.printStackTrace();
            } finally {
                System.out.println("무조건 실행됩니다.");
            }
        }
    }
    
    public static void divisionMethod() throws ArithmeticException {
        Scanner scan = new Scanner(System.in);
        int value = scan.nextInt();
        System.out.println(9/value);
    }
}


실행 결과

9
1
무조건 실행됩니다.
0
java.lang.ArithmeticException: / by zero
무조건 실행됩니다.
    at Test.divisionMethod(Test.java:19)
    at Test.main(Test.java:7)
3
3
무조건 실행됩니다.


먼저 예외가 발생한 메소드는 divisionMethod이지만 divisionMethod에서는 throws ArithemeticException을 사용하여 호출한 곳에서 에러를 처리하게 예외를 넘겨주었습니다. divisionMethod를 호출하는 곳은 main이고 던져진 예외를 catch에서 받아 처리하고 있습니다.


3. 강제로 예외를 발생시켜서 처리(throw)

자바에서는 특정 상황에서 예외를 강제로 발생시킬 수 있습니다. 먼저 숫자를 0으로 나눴을 때 ArithmeticException이 발생한다는 것을 알 수 있습니다. 그럼 사용자가 0을 입력받았을 때 강제적으로 예외를 발생시켜 처리할 수 있게 만들 수 있습니다.

이 내용은 4. 사용자 예외를 생성하여 처리 내용을 보면 어떤상황에서 강제적으로 발생시키는지 더 이해하고 공감할 수 있습니다.

public class Test {
    public static void main(String[] args) {
        while(true) {
            try {
                divisionMethod();
            } catch(ArithmeticException e) {
                e.printStackTrace();
                e.getMessage();
            } finally {
                System.out.println("무조건 실행됩니다.");
            }
        }
    }
    
    public static void divisionMethod() throws ArithmeticException {
        Scanner scan = new Scanner(System.in);
        int value = scan.nextInt();
        if(value == 0) 
        {
            throw new ArithmeticException("0으로 나눌 수 없습니다.");
        }
        System.out.println(9/value);
    }
}


실행 결과

3
3
무조건 실행됩니다.
0
무조건 실행됩니다.
java.lang.ArithmeticException: 0으로 나눌 수 없습니다.
    at Test.divisionMethod(Test.java:22)
    at Test.main(Test.java:7)
2
4
무조건 실행됩니다.


4. 사용자 예외를 생성하여 처리

자바에서는 정상적으로 동작하지만 비지니스 로직 부분에서는 강제로 예외를 발생시켜야 할 상황이 생기고 자바에서는 특정 상황에서의 예외를 작성하여 발생시킬 수 있습니다.


사용자 예외를 생성하기

예외를 생성하는 법은 간단합니다 Exception을 상속받기만 하면됩니다.



class InsertZeroException extends Exception {
    public InsertZeroException() {
        super("0을 입력할 수 없습니다.");
    }
}


자바에서는 0으로 나눴을 때 ArithemeticException 발생한 다는 것을 알 수 있습니다. 그럼 0자체를 입력받았을 때 사용자가 만든 Exception을 발생시키는 예제를 만들어 보았습니다.

class InsertZeroException extends Exception {
    public InsertZeroException() {
        super("0을 입력할 수 없습니다.");
    }
}

public class Test {
    public static void main(String[] args) {
        while(true) {
            try {
                divisionMethod();
            } catch(InsertZeroException e) {
                e.printStackTrace();
                e.getMessage();
            } finally {
                System.out.println("무조건 실행됩니다.");
            }
        }
    }
    
     public static void divisionMethod() throws InsertZeroException {
            Scanner scan = new Scanner(System.in);
            int value = scan.nextInt();
            if(value == 0) 
            {
                throw new InsertZeroException();
            }
            System.out.println(9/value);
    }
}


실행 결과

9
1
무조건 실행됩니다.
0
무조건 실행됩니다.
InsertZeroException: 0을 입력할 수 없습니다.
    at Test.divisionMethod(Test.java:28)
    at Test.main(Test.java:13)



많은 분들이 Throws와 Throw를 혼동하고 있습니다. 이 두가지는 Exception을 발생시킨다는 공통점이 있지만 Throws는 자신을 호출한 상위 메소드로 Exception을 발생시킨다는 것

Throw는 강제로 에러를 발생시키고자 할 때 사용합니다.(현재 메소드 또는 상위 메소드)