자바에서는 서로 다른 두가지 방싱의 exception handling
메커니즘을 가진다. 전통적인 방식은 try/catch/finally
구문이다.
try
는 exception handling
을 위한 코드를 항상 시작시킨다(establishe). try
는 catch
구문을 0개 이상 동반한다.
동반되는 각각의 catch
는 하나 이상의 서로 다른 exception
을 처리할 수 있다. 여러개의 Exception
을 처리할 때는 |
을 통해 구분하자. 항상 Throwable
의 하위 클래스를 캐치할 수 있다. Exception
이 발생했을 때 발생한 타입에 맞는 catch
문을 찾는다.
finallly
는 try
가 발생하면 무조건 실행되는 구문이다. catch
와 마찬가지로 생략되어도 상관없다. 대부분 try
에서 시도한 코드들을 수습할 때 사용한다고 한다.
문법을 살펴보자
try {
}
catch (SomeException e1) {
}
catch (AnotherException | YetAnother Exception e2) {
}
finally {
}
문법부터 보자
try (InputStream is = new FileInputStream("/Users/ben/details.txt")) {
}
try
의 괄호 안에는 cleanup
이 필요한 모든 객체가 파라미터로 들어올 수 있다. 파라미터로 들어온 객체는 try
블록에서만 스코프를 지닌다. 블록이 끝나면 자동으로 cleanup
된다. python의 with 같은 녀석인가봄
좋은 녀석이라고 하니까 자주 사용합시다.
자바 예외 핸들링은 checked
, unchecked
2가지 타입의 예외로 나뉜다.
두 타입의 차이점은 예외가 throw
를 해줘야하는가 아닌가에 달렸다.
checked exception
은 상당히 구체적인 상황에서 발생한다.
어플리케이션이 예외로부터 부분적으로나 완전하게 타격을 받지 않을 수 있다.
예를들어, 여러 개의 디렉토리 중 하나에서 설정 파일을 찾아야하는 코드를 작성 했을 때 존재하지 않는 디렉토리에 접근을 시도하면 FileNotFoundException
이 throw
된다.
우리는 방금 발생한 FileNotFoundException
을 catch
하고 다음 디렉토리로 이동하고 싶을 것이다.
즉, 파일이 존재하지 않는 예외는 충분히 예상 가능하고 있을 법한 예외이며 치명적이지 않기 때문에 타격을 받지 않고 다음 동작으로 넘어갈 수 있다는 뜻이다.
checked exception
의 경우 예상 가능하기 때문에 우리가 직접 try/catch
문으로 예외를 핸들링해줘야만한다.
checked exception
과는 다르게 런타임 조건과 라이브러리 코드의 남발로 쉽게 예측할 수 없는 에러들이 존재한다.
예를들어 OutOfMemoryError
나 NullPointerException
같은 경우에는 쉽게 예측할 수 없다. 사용자가 어떤 입력 값을 입력할지는 모르는 법이고 배열이나 객체를 사용하는 모든 메소드들은 Null
을 넘겨줄 수 있기 때문이다.
이런 것들을 unchecked exception
이라고 하고 언제든지 어떤 메소드던지간에 unchecked exception
을 던질 수 있다.
unchecked
든 checked
든 Throwable
객체이며 Error
, Exception
중 하나의 서브클래스이다.
unchecked
는 Error
객체의 서브 클래스이다. 또한 RuntimeException
객체의 서브클래스이기도 한다.
그 외를 제외한 모든 exception
들은 checked exception
이다.
Error
는 컴파일 오류, 스택오버플로우, 메모리 부족, Null
과 같이 복구하기가 어려운 치명적인 상황을 말한다.
Error
는 개발자가 미리 예측할 수가 없고 설사 예측하더라도 복구하기 어려운 문제이기 때문에 try/catch
등으로 해결하려고 노력하지 말자.
Exception
은 위의 예시와 같이 파일 탐색 프로그램이 접근하고자하는 파일이 존재하지 않을 때처럼 치명적이지 않은 상황을 말한다.
따라서 Exception
은 try/catch
로 프로그램이 의도와 다르게 종료되지 않도록 처리를 해줘야한다.
Scanner sc = new Scanner(System.in);
int[] arr = {0, 1}; # 배열의 길이는 2
try {
for (int i=0; i<2; i++) {
# 1이상의 숫자를 입력 시 ArrayIndexOutOfBoundsException 발생
System.out.println(arr[sc.nextInt()]);
}
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
System.out.println("Gotcha!!");
Runtime Exception
은 언제든지 발생할 수 있는 예외이다. 이러한 예외들은 버그가 발생하지 않으면 버그가 없으므로 일일이 throws
를 작성할 필요가 없다.
오히려 RuntimeException
은 catch
를 하지 않는 것이 바람직할 수 있다. 치명적인 오류이기 때문에 프로그램을 종료시키는 것이 최선의 방책이라고 할 수 있다.
그렇지 않은 것들은 충분히 예상이 가능하기 때문에 예외처리를 통해 프로그램이 종료되는 것을 방지해야한다.
Exception
을 상속받은 클래스를 정의하면 된다.
정의한 클래스를 try/catch
문에서 throw
하면 동작까지 완성이다.
# 내가 정의한 Exception
public class MyException extends Exception {
public MyException() {
super();
}
public void printError() {
System.out.println("MyError 발동!");
}
}
...
...
try {
if (hasError()) {
throw new MyException();
}
} catch (MyException e) {
e.printError();
}