Exception
프로그램에서 예외(Exception)는 실행 중 발생하는 예상치 못한 비정상적인 상황이나 오류를 칭한다.
Java에서는 예외 역시 객체로 취급한다. 따라서 클래스로 정의한다. Exception의 루트 클래스는 java.lang.Throwable이다.
Object <- Throwable <-+- Exception <-+--IOException
| |
| .
| .
|
+- Error <-+------LinkageError
|
.
.
Error, Exception 등 이름 때문에 헷갈릴 수 있지만 모두 런타임에 발생하는 예외다.
Throwable 클래스는 예외 클래스의 루트다. 모든 Java 예외 클래스는 Throwable을 직간접적으로 상속한다.
Exception 클래스나 그 subclass를 상속함으로써 커스텀 예외 클래스도 만들 수 있다.
예외 클래스는 크게 3가지 타입으로 나뉜다.
System errors
JVM에 의해 던져지고 Error 클래스에서 다뤄진다. Error 클래스는 내부 시스템 오류를 묘사하지만 이러한 오류는 거의 발생하지 않는다. 만약 발생한다면 사용자에게 알림을 보내고 프로그램을 살포시 종료하는 것 외에 우리가 할 수 있는 것은 없다.
Error의 subclass 예시
Exceptions
Exception 클래스에서 우리가 작성한 프로그램이나 외부 환경에 의해 발생하는 오류를 다룬다. 이런 오류들은 당신의 프로그램에게 잡혀서 핸들링 된다.
Exception의 subclass 예시
Runtime exceptions
RuntimeException 클래스에서 잘못된 형변환, array 범위를 벗어남, 수치 오류 등 프로그램 실행 중 발생하는 오류를 다룬다. 일반적으로 JVM에 의해 던져진다.
RuntimeException의 subclass 예시
Exception handling
Java의 예외 처리(Exception handling)은 다음 세 단계를 거친다.
예외를 선언하기 위해서는 throws 키워드를 메소드의 헤더에 사용할 수 있다.
public void myMethod() throws IOException { ... }
throws 키워드는 myMethod가 IOException을 던질 수 있음을 나타낸다. 만약 메소드가 다수의 예외를 던질 수 있다면 콤마(,)로 구분된 예외 리스트를 추가한다.
public void myMethod() throws Exception1, Exception2, ..., ExceptionN
Throwing Exceptions
오류를 감지한 프로그램은 적절한 예외 인스턴스를 생성해서 던질 수 있다. 이 것이 throwing an exception이라고 알려진 동작이다.
예를 들어, 프로그램이 메소드 정의와 맞지 않는 잘못된 인자가 들어왔음을 감지했다면 IllegalArgumentException 인스턴스를 생성해서 던진다.
IllegalArgumentException ex = new IllegalArgumentException("Wrong Argument");
throw ex;
또는
throw new IllegalArgumentException("Wrong Argument");
Catching Exceptions
던져진 예외는 try-catch 블럭에서 핸들링 된다.
try {
statements; // 예외 발생 가능성이 있는 구문
} catch (Exception1 ex1) {
handler for ex1;
} catch (Exception2 ex2) {
handler for ex2;
}
...
} catch (ExceptionN exN) {
handler for exN;
}
우리는 다음과 같이 메소드가 중첩되어 호출되는 시나리오를 가정해볼 것이다.
main 메소드에서 m1을 호출m1에서 m2 호출m2에서 m3 호출m3에서 예외 던짐public static void main(String[] args) {
try {
m1();
statement1;
} catch (Exception1 ex1) {
// Process ex1...
}
statement2;
}
public void m1() {
try {
m2();
statement3;
} catch (Exception2 ex2) {
// Process ex2...
}
statement4;
}
public void m2() {
try {
m3(); // 예외 발생!
statement5;
} catch (Exception3 ex3) {
// Process ex3...
}
statement6;
}
만약 발생한 예외 타입이 Exception3라면 m2의 catch 블럭에 잡혀서 핸들링 될 것이다. statement5는 생략되고 바로 statement6으로 넘어간다.
발생한 예외 타입이 Exception2라면 m2는 중단되고 제어는 m1으로 반환된다. 예외는 m1의 catch 블럭에서 처리되고 statement4를 실행한다.
발생한 예외 타입이 Exception1이라면 m1은 중단되고 제어는 main으로 넘어간다. 예외는 main의 catch 블럭에서 처리되고 statement2를 실행한다.
만약 발생한 예외 타입이 m2, m1, main에서 잡을 수 없는 타입이면 프로그램은 강제종료되고 statement1과 statement2는 실행되지 않는다.
예외에서 정보 얻기
Throwable 클래스는 다음과 같은 메소드를 제공한다.
getMessage()toString(){예외 클래스 풀네임}: {getMessage()}로 구성된 문자열을 반환한다.printStackTrace()Throwable 객체와 호출 스택 추적 정보를 콘솔에 출력한다.getStackTrace()finally clause
finally 절은 예외 발생 여부와 관계 없이 항상 실행된다.
try {
statements;
} catch (Exception ex) {
handling ex;
} finally {
finalStatements;
}
언제 예외 처리를 하는가
우리가 예상 못할 오류가 발생할 수 있는 조건을 다뤄야할 때 에러 처리가 필요하다. 하지만, 단순한 조건 검사에는 try-catch 블럭을 사용하지 않는 것을 권장한다.
(try-catch도 오버헤드임)
Rethrowing Exceptions
Java는 핸들러가 예외를 처리하지 못하거나 단순히 호출자에게 예외를 알리고 싶을 때 예외를 다시 던지는 것을 허용한다.
try {
statements;
} catch (Exception ex) {
perform operation before exits;
throw ex;
}
Chained Exceptions
public class Main {
public static void main(String[] args) {
try {
method1();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void method1() throws Exception {
try {
method2();
} catch (Exception ex) {
throw new Exception("New info from method1 ", ex);
}
}
public static void method2() throws Exception {
throw new Exception("New info from method2");
}
}
java.lang.Exception: New info from method1
at Main.method1(Main.java:26)
at Main.main(Main.java:16)
Caused by: java.lang.Exception: New info from method2
at Main.method2(Main.java:31)
at Main.method1(Main.java:24)
... 1 more
Custom Exception Class
// MyException.java
public class MyException extends Exception {
public MyException() {
super("I made my own Exception");
}
}
// Main.java
public class Main {
static void callDriver() throws ClassNotFoundException {
// 예외 발생을 위한 존재하지 않는 클래스 찾기
Class.forName("oracle.jdbc.driver.OracleDriver");
System.out.println("Finished"); // 실행되지 않을 것.
}
static void callException() throws MyException {
// Do something wrong, throw exception.
throw new MyException();
}
public static void main(String[] args) {
try {
callDriver();
} catch (ClassNotFoundException e) {
// 핸들링 예시
System.out.println("Cannot find Class");
}
try {
callException();
} catch (MyException e) {
// Throwable 객체의 메소드를 활용한 방법
System.out.println(e.getMessage());
}
}
}
Cannot find Class
I made my own Exception
<참고자료>
탁성우, "플랫폼기반프로그래밍", 부산대학교