[자바의 정석] CH08 예외처리
자바의 정석 책을 공부하고 정리한 글입니다!
혹시라도 틀린 부분이 있다면 친절하게 알려주세요.
감사합니다!
CH8. 예외처리
프로그램이 실행 중 어떤 원인에 의해 오작동하거나 비정상적으로 종류되는 경우가 있는데, 이를 초래하는 원인을 프로그램 에러 또는 오류라고 합니다.
에러는 발생시점에 따라 컴파일 에러와 런타임 에러로 나눌 수 있으며, 이 외에도 논리적 에러가 있습니다.
- 컴파일 에러: 컴파일 시에 발생하는 에러
- 런타임 에러: 실행 시에 발생하는 에러
- 논리적 에러: 실행은 되지만, 의도와 다르게 동작하는 것
소스코드를 컴파일 하면 컴파일러는 소스코드에 대해 오타나 잘못된 구문을 알려 줍니다. 컴파일러가 알려준 오류를 모두 수정하면 컴파일을 성공적으로 마치게 되어, 클래스 파일이 생성되며, 생성된 클래스 파일을 실행할 수 있게 되는 것이죠.
하지만 컴파일러는 실행 도중에 발생할 수 있는 잠재적 오류까지 검사할 수 없기 때문에, 컴파일이 잘 되었어도 실행 중에 에러가 발생할 수 있습니다.
따라서 이를 방지하기 위해서는 프로그램의 실행 도중 발생할 수 있는 모든 경우의 수를 고려하여 이에 대한 대비를하는 것이 필요합니다. 자바에서는 실행 시 발생할 수 있는 프로그램 오류를 에러와 예외로 구분합니다.
- 에러(error): 프로그램 코드에 의해 수습될 수 없는 심각한 오류
- 예외(exception): 프로그램 코드에 의해 수습될 수 있는 미약한 오류
또한 자바에서는 에러와 예외를 각각 Error, Exception 클래스로 정의하였습니다.
예외처리하기: try-catch문
예외처리란 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것으로, 예외의 발생으로 인한 실행 중인 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는 것이 목적입니다.
만약 발생한 예외를 처리하지 못하면, 프로그램은 비정상적으로 종료되며, JVM의 예외처리기(UncaughtExceptionHandler)가 받아서 예외의 원인을 화면에 출력합니다.
예외를 처리하기 위해서는 try-catch문을 사용하며 구조는 다음과 같습니다.
try{
// 예외가 발생할 가능성이 있는 문장
} catch (Exception1 e1) {
// Exception1이 발생했을 경우 처리될 문장
} catch (Exception2 e2) {
// Exception1이 발생했을 경우 처리될 문장
}
하나의 try블럭 다음에는 하나 이상의 cath블럭이 올 수 있으며, 예외가 발생하면 예외의 종류와 일치하는 단 한 개의 catch블럭만 수행됩니다. 발생한 예외의 종류와 일치하는 catch블럭이 없으면 예외는 처리되지 않습니다.
try-catch문은 예외가 발생한 경우와 발생하지 않았을 경우에 따라 문장 실행순서가 달라집니다.
- try블럭 내에서 예외가 발생한 경우:
- 발생한 예외와 일치하는 catch블럭 확인
- 일치하는 catch블럭을 찾게 되면, 해당 catch문 내의 문장을 수행한 후, try-catch문을 빠져나감.
- 일치하는 catch문을 찾지 못하면 예외는 처리되지 못함 - try블럭 내에서 예외가 발생하지 않은 경우: catch블럭을 거치지 않고 try-catch문을 빠져나감
[참고] try블럭이나 catch블럭은 포함된 문장이 하나뿐이어도 괄호를 생략할 수 없습니다.
JDK1.7부터는 여러 catch블럭을 | 기호를 사용하여 하나의 catch블럭으로 합칠 수 있게 되었으며, 이를 멀티 catch블럭이라 합니다. 멀티 catch블럭을 사용하면 중복된 코드를 줄일 수 있습니다.
만약 멀티 catch블럭의 | 기호로 연결된 예외 클래스가 부모-자식 관계에 있다면 컴파일 에러가 발생합니다.
예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨 있으며, printStackTree()와 getMessage()를 통해 예외에 대한 정보를 얻을 수 있습니다.
- printStackTree(): 예외발생 당시의 호출스택에 있었던 메서드의 정보와 예외 메시지를 출력한다.
- getMessage(): 발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.
예외 발생시키기
throw키워드를 사용하면 프로그래머가 고의로 예외를 발생시킬 수 있으며 방법은 다음과 같습니다.
- new 연산자를 사용하여 발생시키려는 예외 클래스 객체를 만든다.
- throw 키워드를 사용하여 예외를 발생시킨다.
try {
Exception e = new Exception("고의로 발생시킴");
throw e;
// 위의 두 문장을 throw new Exception("고의로 발생시킴"); 로 줄여 쓸 수도 있습니다.
} catch (Exception e) {
System.out.println("에러 발생");
}
메서드에 예외 선언하기
try-catch문 외에도 예외를 메서드에 선언하는 방법도 있습니다. 메서드에 예외를 선언하려면 메서드의 선언부에 throws 키워드를 사용해 메서드 내에서 발생할 수 있는 예외를 적어주면 됩니다.
void method() throws Exception1, Exception2, ... {
// 메서드 내용
}
사실 메서드에 throws를 명시하는 것은 예외를 처리하는 것이 아닌, 자신을 호출한 메서드에게 예외를 전달하여 예외처리를 떠맡기는 것입니다.
예외를 전달받은 메서드가 또다시 자신을 호출한 메서드에게 전달할 수 있습니다. 이런 식으로 계속 호출 스택에 있는 메서드들을 따라 전달되다가 제일 마지막에 있는 main메서드에서도 예외가 처리되지 않으면, 프로그램의 전체가 종료됩니다.
public class Main{
public static void main(String[] args) throws Exception {
method1();
}
private static void method1() throws Exception {
method2();
}
private static void method2() throws Exception {
throw new Exception();
}
}
finally 블럭
finally블럭은 예외의 발생여부에 상관없이 실행되어야할 코드를 포함시킬 목적으로 사용됩니다. try-catch문의 끝에 선택적으로 덧붙여 사용할 수 있으며 try-catch-finally의 순서로 구성됩니다.
try {
// 예외 발생 가능성이 있는 문장
} catch (Exception e1) {
// 예외처리를 위한 문장
} finally {
// 예외의 발생여부에 관계없이 항상 수행되어야하는 문장
// 이 블럭은 맨 마지막에 위치해야 한다.
}
실행 순서는 다음과 같습니다:
- 예외가 발생한 경우: try -> catch -> finally 순서
- 예외가 발생하지 않은 경우: try -> finally
finally예시는 다음과 같습니다.
public class Main{
public static void main(String[] args) {
method1();
System.out.println("method1의 수행을 마치고 main메서드로 돌아옴");
}
private static void method1() {
try {
System.out.println("method1 호출");
return;
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("method1의 finally 블럭 실행");
}
}
}
위의 결과에서 알 수 있듯이, try 블럭에서 return문을 실행하는 경우에도 finally블럭의 문장이 먼저 실행된 후에, 현재 실행 중인 메서드를 종료합니다. 이와 마찬가지로 catch블럭의 문장 수행 중에 return을 만나도 finally블럭의 문장들은 수행됩니다.
사용자 정의 예외 만들기
필요에 따라 프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있으며, 다음과 같이 정의할 수 있습니다.
class MyException extends Exception {
MyException(String msg) {
super(msg); // 조상인 Exception클래스의 생성자 호출
}
}
보통 Exception클래스 또는 RuntimeException클래스로부터 상속받아 클래스를 만들지만 필요에 따라 알맞은 예외 클래스를 선택할 수 있습니다.