Java 07. 예외처리 - swkim0128/PARA GitHub Wiki
프로그램이 실행 중 어떤 원이에 의해서 오작동을 하거나 비정상적으로 종료되는 경우가 있다. 이러한 결과를 초래하는 원인을 프로그램 에러 똔느 오류라고 한다.
이를 발생시점에 따라 '컴파일 에러(compile-time error)'와 '런타임 에러(runtime-error)'로 나눌 수 있는데, 글자 그대로 '컴파일 에러'는 컴파일 할 때 발생하는 에러이고 프로그램의 실행 도중에 발생하는 에러를 '런타임 에러'라고 한다.
자바에서는 실행 시(runtime) 발생할 수 있는 프로그램 오류를 '에러(error)'와 '예외(exception)', 두 가지로 구분하였다.
에러는 메모리 부족(OutOfMemoryError)이나 스택오버플로우(StackOverflowError)와 같이 일단 발생하면 복구할 수 없는 심각한 오류이고, 예외는 발생하더라도 수습될 수 있는 비교적 덜 심각한 것이다.
에러(error) - 프로그램 코드에 으해서 수습될 수 없는 심각한 오류
예외(exception) - 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
프로그램의 실행 도중에 발생하는 에러는 어쩔 수 없지만, 예외는 프로그래머가 이에 대한 처리를 미리 해주어야 한다.
예외처리(exception handling)란, 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이며, 예외처리의 목적은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행 상태를 유지할 수 있도록 하는 것이다.
예외처리(excption handling)의
정의 - 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것
목적 - 프로그램의 비정상 종료를 막고, 정사적인 실행 상태를 유지하는 것
try {
// 예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch (Exception1 e1) {
// Exception1이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch (Exception2 e2) {
// Exception2가 발생했을 경우, 이를 처리하기 위한 분장을 적는다.
...
} catch (ExceptionN eN) {
// ExceptionN이 발생했을 경우, 이를 처리하기위한 문장을 적는다.
}
하나의 try블럭 다음에는 다음에는 여러 종류의 예외를 처리할 수 있도록 하나 하나 이상의 catch블럭이 올 수 있으며, 이 중 발생한 예외의 종류와 일치하는 단 한 개의 catch블럭만 수행된다.
▶︎ try블럭 내에서 예외가 발생한 경우,
- 발생한 예외의 일치하는 catch블럭이 있는지 확인한다.
- 일치하는 catch블럭을 찾게되면, 그 catch블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 만일 일치하는 catch블럭을 찾지 못하면, 예외는 처리되지 못한다.
▶︎ try블럭 내에서 예외가 발생하지 않은 경우,
- catch블럭을 가지지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.
키워드 throw를 사용해서 프로그래머가 고의로 예외를 발생시킬 수 있으며, 방법은 아래의 순서를 따르면 된다.
- 먼저, 연산자 new를이용해서 발생시키려는 예외 클래스의 객체를 만든 다음
Exception e = new Exception("고의로 발생시켰음");- 키워드 throw를 이용해서 예외를 발생시킨다.
throw e;
자바에서는 실행 시 발생할 수 있는 오류(Exception과 Error)를 클래스로 정의하였다. 모든 클래스의 조상은 Object클래스이므로 Exception과 Error클래스 역시 Obejct클래스의 자손들이다.
- RuntimeException클래스와 그 자손클래스들
- Exception클래스와 그 자손 클래스들
RuntimeException클래스들은 주로 프로그래머의 실수에 의해서 발생될 수 있는 예외들로서 자바의 프로그래밍 요소들과 관계가 깊다.
RuntimeException예외들이 발생할 가능성이 있는 코드들은 try-catch문을 사용하기 보다는 프로그래머들이 보다 주의 깊게 작성하여 예외가 발행하지 않도록 해야할 것이다.
Exception클래스들은 주로 외부의 영향으로 발생할 수 있는 것들로서, 프로그램의 사용자들의 동작에 의해서 발생한는 경우가 많다. 예를 들면, 존재하지 않는 파일의 이름을 입력했다던가(FileNotFoundException), 실수로 클래스의 이름을 잘못 적었다던가(ClassNotFoundException), 또는 입력한 데이터 형식이 잘못된(DataFormatException)경우에 발생하는 예외들이다.
RuntimeException클래스들 - 프로그래매의 실수로 발생하는 예외
Exception클래스들 - 사용자의 시룻와 같은 외적인 요인에 의해 발생하는 예외
catch블럭은 괄호()와 블럭{} 두 부분으로 나눠져 있는데, 괄호()내에는 처리하고자 하는 예외와 같은 타입의 참조변수 하나를 선언해야한다.
예외가 발생하면, 발생한 예외에 해당하는 클래스의 인스턴스가 만들어 진다. 예외가 발생한 문장이 try-catch문의 try블럭에 포함되어 있다면, 이 예외를 처리할 수 있는 catch블럭이 있는지 찾게 된다.
첫 번째 catch블럭부터 차례로 내려가면서 catch블럭의 괄호()내에 선언된 참조변수의 종류와 생성된 예외클래스의 인스턴스에 instanceof연산자를 이용해서 검사하게 되는데, 검사결과가 true인 catch블럭을 만날 때까지 검사는 계속된다.
검사결과가 true인 catch블럭을 찾게 되면 블럭에 있는 문장들을 모두 수행한 후에 try-catch문을 빠져나가고 예외는 처리되지만, 검사결과가 true인 catch블럭이 하나도 없으면 예외는 처리되지 않는다.
모든 예외 클래스는 exception클래스의 자손이므로, catch블럭의 괄호()에 Exception클래스 타입의 참조변수를 선언해 놓으면 어떤 종류의 예외가 발생하더라도 이 catch블럭에 의해서 처리된다.
try-catch문의 마지막에 Exception클래스 타입의 참조변수를 선언한 catch블럭을 사용하면, 어떤 종류의 예외가 발생하더라도 이 catch블럭에 의해 처리되도록 할 수 있다.
예외가 발생했을 때 생성되는 예외클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨져 있으며, getMessage()와 printStackTrace()를 통해서 이 정보들을 얻을 수 있다.
printStackTrace() - 예외발생 당시의 호출스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력한다.
getMessage() - 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.
try-catch문으로 예외처리를 하여 예외가 발생해도 비정상적으로 종료하지 않도록 해주는 동시에, printStackTrace() 또는 getMEssage()와 같은 메서드를 통해서 예외의 발생원인을 알 수 있다.
그리고 printStackTrace(PrintStream s) 또는 printStackTrace(PrintWriter s)를 사용하면 발생한 예외에 대한 정보를 파일에 저장할 수도 있다.
finally블럭은 try-catch문과 함께 예오의 발생여부에 상관없이 실행되어야할 코드를 포함시킬 목적으로 사용된다.
try {
// 예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch (Exception1 e1) {
// 예외처리를 위한 문장을 적는다.
} finally {
// 예외의 발생여부에 관계없이 항상 수행되어야하는 문장들을 넣는다.
// finally블럭은 try-catch문의 맨 마지막에 위치해야한다.
}
예외가 발생한 경우에는 'try → catch → finally'의 순으로 실행되고, 예외가 발생하지 않은 경우에는 'try → finally'의 순으로 실행된다.
void method() throws Exceptions1, Exception2, ... ExceptionN {
...
}
예외를 메서드의 throws에 명시하는 것은 예외를 처리하는 것이 아니라, 자신(예외가 발생할 가능성이 있는 메서드)을 호출한 메서드에게 예외를 전달하여 예외처리를 떠맡기는 것.
예외를 전달받은 메서드가 또다시 자신을 호출한 메서드에게 전달할 수 있으며, 이런 식으로 계속 호출스택에 있는 메서들을 따라 전달되다가 제일 마지마겡 있는 main메서드에서도 예외가 처리되지 않으면, Main메서드 마저 종료되어 프로그램이 전체가 종료된다.
한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 try-catch문을 통해서 메서드 내에서 자체적으로 처리하고 그 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써, 양쪽에서 나눠서 처리되도록 할 수 있다.
이것은 예외를 처리한 후에 인위적으로 다시 발생시키는 방법을 통해서 가능한데, 이것을 '예외 되던지기(exception re-throwing)'이라고 한다.
먼저 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 상요해서 예외를 처리해주고 catch문에서 필요한 작업을 행한 후에 trhow문을 사용해서 예외를 다시 발생시킨다. 다시 발생한 예외는 이 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 또다시 처리한다.
프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있다.
class MyException extends Exception {
MyException(String msg) { // 문자열을 매개변수로 받는 생성자
super(msg); // 조상인 exception클래스의 생성자를 호출한다.
}
}
class MyException extends Exception {
// 에러 코드 값을 저장하기 위한 필드를 추가했다.
private final int ERR_CODE; // 생성자를 통해 초기화 한다.
MyException(String msg, int errCode) { // 생성자
super(msg);
ERR_CODE = errCode;
}
MyException(String msg) { // 생성자
this(msg, 100); // ERR_CODE를 100(기본값)으로 초기화한다.
}
public int getErrCode() { // 에러 코드를 얻을 수 있는 메서드도 추가했다.
return ERR_CODE; // 이 메서드는 주로 getMessage()와 함꼐 사용될 것이다.
}
}