본문

예외처리

반응형

# 예외처리

- 정의 : 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것

- 목적 : 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것


Source01) exceptionEx01.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package exceptionEx;
 
public class exceptionEx01 {
 
    public static void main(String[] args) {
        int number = 100;
        int result = 0;
 
        for (int i = 0; i < 10; i++) {
            try {
                result = number / (int) (Math.random() * 10);
                System.out.println(result);
            } catch (ArithmeticException e) {    //ArithmeticException이 발생하면 실행되느 코드
                System.out.println("0");
            }
        }
    }
}
cs

Result)

1
2
3
4
5
6
7
8
9
10
11
100
20
12
0        <-- ArithmeticException이 발생해서 0이 출력되었다.
11
16
20
33
11
33
 
cs


- 예외 발생시키기
키워드 throw를 사용해서 프로그래머가 고의로 예외를 발생시킬 수 있다.

1. 먼저, 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음
Exception e = new Exception("고의로 발생시켰음");

2. 키워드 throw를 이용해서 예외를 발생시킨다.
throw e;

Source01) throwEx01.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package try_catch;
 
public class throwEx01 {
 
    public static void main(String[] args) {
        try {
            Exception e = new Exception("고의로 발생시켰음.");
            throw e; // 예외를 발생시킴
            // 위의 두 줄을 한 줄로 줄여 쓸 수 있다.
            // throw new Exception("고의로 발생시켰음");
 
        } catch (Exception e) {
            System.out.println("에러 메시지 : " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println("프로그램이 정상 종료되었음.");
    }
 
}
cs

Result)
1
2
3
4
에러 메시지 : 고의로 발생시켰음.
java.lang.Exception: 고의로 발생시켰음.
    at try_catch.throwEx01.main(throwEx01.java:7)
프로그램이 정상 종료되었음.
cs

Exception인스턴스를 생성할 때, 생성자에 String을 넣어 주면, 이 String이 Exception인스턴스에 메세지로 저장된다. 
이 메세지는 getMessage()를 이용해서 얻을 수 있다.


- Finally 블럭
finally블럭은 try-catch문과 함께 예외의 발생여부에 상관없이 실행되어야할 코드를 포함시킬 목적으로 사용된다.

try {
// 예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch {
// 예외처리를 위한 문장을 적는다.
} finally {
// 예외의 발생여부에 관계없이 항상 수행되어야하는 문장들을 넣는다.
// finally블럭은 try-catch문의 맨 마지막에 위치한다.
}



Source01) finallyEx02.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package finallyEx;
 
public class finallyEx02 {
 
    public static void main(String[] args) {
        // method1()은 static 메서드이므로 인스턴스 생성없이 직접 호출이 가능하다.
        finallyEx02.method1();
        System.out.println("method1()의 수행을 마치고 main메서드로 돌아왔습니다.");
    }
 
    private static void method1() {
        try {
            System.out.println("method1이 호출되었습니다.");
 
            // 현재 실행중인 메서드를 종료
            // finally가 수행된 후 가장 마지막으로 return 수행
            return;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("method1()의 finally블럭이 실행되었습니다.");
        }
    }
 
}
cs

Result)
1
2
3
method1이 호출되었습니다.
method1()의 finally블럭이 실행되었습니다.
method1()의 수행을 마치고 main메서드로 돌아왔습니다.
cs

try블럭에서 return문이 실행되는 경우에도 finally블럭의 문장들이 먼저 실행된 후에, 현재 실행 중인 메서드를 종료한다.
이와 마찬가지로 catch블럭의문장 수행중에return문을 만나도 finally블럭 문장들은 수행된다.


- 메서드에 예외 선언하기 (throw : 강제 예외 발생, throws : 메서드에 예외 선언)
예외를 처리하는 방법에는 지금까지 배워 온 try-catch문을 사용하는 것 이외에, 예외를 메서드에 선언하는 방법이 있다.
메서드에 예외를 선언하려면, 메서드의 선언부에 키워드 throws를 사용해서 메스드 내에서 발생할 수 있는 예외를 적어주기만 하면 된다.
그리고, 예외가 여러 개일 경우에는 쉼표(,)로 구분한다.
1
2
3
void method() throws Exception1, Exception2,... ExceptionN {
    // 메서드 내용
}
cs

이렇게 메서드의 선언부에 예외를 선언함으로써 메서드를 사용하려는 사람이 메서드의 선언부를 보았을 때, 이 메서드를 사용하기 위해서는 어떠한 예외들이 처리되어져야 하는지 쉽게 알 수 있다.

메서드에 예외를 선언할 때 일반적으로 RuntimeException클래스들은 적지 않는다. 이들을 메서드 선언부의 throws에 선언한다고 해서 문제가 되지는 않지만, 보통 반드시 처리해주어야 하는 예외들만 선언한다. (RuntimeException은 예외처리 해주지 않아도 컴파일 시 문제가 되지 않는다.)

사실 예외를 메서드의 throws에 명시하는 것은 예외를 처리하는 것이 아니라, 자신을 호출한 메서드에게 예외를 전달하여 예외처리를 떠맡기는 것이다. 예외를 전달받은 메서드가 또다시 자신을 호출한 메서드에게 전달할 수 있으며, 이런 식으로 계속 호출스택에 있는 메서드들을 따라 전달되다가 제일 마지막에 있는 main메서드에서도 예외가 처리되지 않으면, main메서드 마저 종료되어 프로그램 전체가 종료된다.

Source01) ExceptionEx07.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package try_catch;
 
public class ExceptionEx07 {
 
    public static void main(String[] args) throws Exception {
        // 같은 클래스내의 static멤버이므로 객체생성없이 직접 호출 가능
        method1();
    }
 
    private static void method1() throws Exception {
        method2();
    }
 
    private static void method2() throws Exception {
        throw new Exception();    // 예외발생시킴
    }
 
}
cs

Result)
1
2
3
4
Exception in thread "main" java.lang.Exception
    at try_catch.ExceptionEx07.method2(ExceptionEx07.java:15)
    at try_catch.ExceptionEx07.method1(ExceptionEx07.java:11)
    at try_catch.ExceptionEx07.main(ExceptionEx07.java:7)
cs

- 예외가 발생했을 때, 모두 3개의 메서드(main, method1, method2)가 호출스택에 있었으며,
- 예외가 발생한 곳은 제일 윗줄에 있는metho2()라는 것
- main메서드가 method1()을, 그리고 method1()은 method2()를 호출했다는 것을 알 수 있다.

위의 예제를 보면, method2()에서 'throw new Exception();'문장에 의해 예외가 강제적으로 발생했으나 try-catch문으로 예외처리를 해주지 않았으므로, method2()는 종료되면서 예외를 자신을 호출한 method1()에게 넘겨준다. method1()에서는 역시 예외처리를 해주지 않았으므로 종료되면서 main메서드에게 예외를 넘겨준다.
그러다 main메서드에서 예외처리를 하지 않고 자신을 호출한 메서드에게 예외를 넘겨줄 수는 있지만,이것으로 예외가 처리된 것은 아니고 예외를 단순히 전달만 하는 것이다. 결국 어느 한 곳에서는 반드시 try-catch문으로 예외처리를 해주어야한다.

Source01) ExceptionEx09.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package try_catch;
 
public class ExceptionEx09 {
 
    public static void main(String[] args) {
        try {
            method1();
        } catch (Exception e) {
            System.out.println("main메서드에서 예외가 처리되었습니다.");
            e.printStackTrace();
        }
    }
 
    private static void method1() throws Exception {
        throw new Exception();
    }
 
}
cs

Result)
1
2
3
4
main메서드에서 예외가 처리되었습니다.
java.lang.Exception
    at try_catch.ExceptionEx09.method1(ExceptionEx09.java:15)
    at try_catch.ExceptionEx09.main(ExceptionEx09.java:7)
cs


Source02) ExceptionEx10.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package try_catch;
 
import java.io.File;
 
public class ExceptionEx10 {
 
    public static void main(String[] args) {
        try {
            File f = createFile(args[0]);
            System.out.println(f.getName() + "파일이 성공적으로 생성되었습니다.");
        } catch (Exception e) {
            System.out.println(e.getMessage() + "다시 입력해 주시기 바랍니다.");
        }
    }
 
    private static File createFile(String fileName) throws Exception {
        if (fileName == null || fileName.equals("")) {
            throw new Exception("파일이름이 유효하지 않습니다.");
        }
 
        File f = new File(fileName); // File클래스의 객체를 만든다.
 
        // File객체의 createNewFile메서드를 이용해서 실제 파일을 생성한다.
        f.createNewFile();
        
        return f; // 생성된 객체의참조를 반환한다.
    }
}
cs

Result)
1
2
>java ExceptionEX10 test.txt
test.txt파일이 성공적으로 생성되었습니다.
cs

1
2
>java ExceptionEX10 ""
파일이름이 유효하지 않습니다. 다시 입력해 주시기 바랍니다.
cs

이처럼 예외가 발생한 메서드 내에서 자체적으로 처리해도 되는 것은 메서드 내에서 try-catch문을 사용해서 처리하고,
ExceptionEX10 예제처럼 메서드에 호출 시 넘겨받아야 할 값(fileName)을 다시 받야야 하는 경우(메서드 내에서 자체적으로 해결이 안 되는 경우)에는 예외를 메서드 안에 선언해서, 호출한 메서드에서 처리해야한다.


- 예외 되던지기(exception re-throwing)
먼저 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 사용해서 예외를 처리해주고 catch문에서 필요한 작업을 행한 후에 throw문을 사용해서 예외를 다시 발생시킨다. 다시 발생한 예외는 이 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 또 다시 처리한다.

이 방법은 하나의 예외에 대해서 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해줘야 할 작업이 있을 때 사용된다.
이 때 주의해야할 점은 예외가 발생할 메서드에서는 try-catch문을 사용해서 예외처리를 해줌과 동시에 메서드의 선언부에 발생할 예외를 throws에 지정해줘야 한다는 것이다.

Source01) ExceptionEx11.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package try_catch;
 
public class ExceptionEx11 {
 
    public static void main(String[] args) {
        try {
            method1();
 
        } catch (Exception e) {
            System.out.println("main메서드에서 예외가 처리되었습니다.");
        }
    }
 
    private static void method1() throws Exception {
        try {
            throw new Exception();
        } catch (Exception e) {
            System.out.println("method1메서드에서 예외가 처리되었습니다.");
            throw e; // 다시 예외를 발생시킨다.
        }
 
    }
 
}
cs

Result)
1
2
method1메서드에서 예외가 처리되었습니다.
main메서드에서 예외가 처리되었습니다.
cs




출처 : JAVA의정석(남궁성 저)


반응형

공유

댓글