컴퓨터 하드웨어의 오동작 또는 고장으로 인해 응용프로그램에 이상이 생겼거나 JVM 실행에 문제가 생겼을 경우 발생하는 것
프로세스에 영향을 준다
시스템 레벨에서 발생한다(자바 프로그램 외의 오류)
종류
컴퓨터의 에러가 아닌 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류
예외가 발생하면 프로그램이 종료가 된다는 것은 에러와 동일하지만 예외는 예외처리(Exception Handling)를 통해 프로그램을 종료되지 않고 정상적으로 작동되게 만들어줄 수 있다. 자바에서 예외처리는 Try Catch문을 통해 해줄 수 있다.
개발자가 구현한 로직에서 발생
쓰레드에 영향을 준다
예외는 메서드의 파라미터나 반환 값만큼이나 중요한 공용 인터페이스 중 하나이다.
메서드를 호출하는 쪽은 그 메서드가 어떤 예외를 발생시킬 수 있는가에 대해 반드시 알아야 한다. 따라서 Java는 checked exception을 통해 해당 메서드가 발생시킬 수 있는 예외를 명세하도록 강제하고 있다.
Runtime Exception은 프로그램 코드의 문제로 발생하는 예외이다. 따라서 클라이언트 쪽(메서드를 호출하는 쪽)에서 이를 복구(or 회복)하거나 대처할 수 있을 거라고 예상하긴 어렵다.
Runtime Exception은 프로그램 어디서나 매우 빈번하게 발생할 수 있기 때문에 모든 Runtime Exception을 메서드에 명시하도록 강제하는 것은 프로그램의 명확성을 떨어뜨릴 수 있다.
클라이언트가 exception을 적절히 회복할 수 있을 것이라고 예상되는 경우 checked exception으로 만들고, 그렇지 않은 경우 unchecked exception으로 만드는 것이 좋다.
for (String item : items) {
try {
insert(item);
}catch (SQLException e) {
e.printStackTrace();
}
}
메소드를 선언할 때 매개 변수 소괄호 뒤에 throws라는 예약어를 적어 준 뒤 예외를 선언하면, 해당 메소드에서 선언한 예외가 발생했을 때 호출한 메소드로 예외가 전달된다.
만약 메소드에서 두 가지 이상의 예외를 던질 수 있다면, implements처럼 콤마로 구분하여 예외 클래스 이름을 적어주면 된다.
try 블록 내에서 예외를 발생시킬 경우에는 throw라는 예약어를 적어 준 뒤 예외 객체를 생성하거나, 생성되어있는 객체를 명시해준다.
throw한 예외 클래스가 catch 블록에 선언되어 있지 않거나, throws 선언에 포함되어 있지 않으면 컴파일 에러가 발생한다.
catch 블록에서 예외를 throw 할 경우에도 메소드 선언의 throws 구문에 해당 예외가 정의되어 있어야만한다.
보통 예외처리는 처리 비용이 비싸다고 한다. try-catch를 동작 하면서 발생하는 검사들도 하나의 원인이겠지만, Throwable 생성자의 fillInStackTrace() 메서드가 주 원인이다.
이 메서드는 예외가 발생한 메서드의 Stack Trace를 모두 출력해주기 때문이다.
StackTrace란 Application이 실행된 시점부터 현재 실행 위치까지의 메서드 호출 목록이다.
JDK 1.7부터 여러 catch block을 하나로 합칠 수 있게 되었다.
public class ExceptionDemo {
public static void main(String[] args) {
try {
System.out.println(1 / 0);
} catch (IllegalArgumentException | ArithmeticException e) {
System.out.println(e.getMessage());
}
}
}
단, 나열된 예외 클래스들이 부모-자식 관계에 있다면 오류가 발생한다.
public class ExceptionDemo {
public static void main(String[] args) {
try {
System.out.println(1 / 0);
} catch (RuntimeException | ArithmeticException e) {
// 에러 발생: ArithmeticException은 RuntimeException을 상속받는 클래스이다.
System.out.println(e.getMessage());
}
}
}
멀티캐치는 하나의 블록으로 여러 예외를 처리하는 것이기 때문에 멀티 캐치 블록 내에서는 발생한 예외가 정확이 어디에 속한 것인지 알 수 없다. 그래서 참조 변수 e에는 '|'로 연결된 예외들의 공통 조상 클래스에 대한 정보가 담긴다.
try(FileOutputStream out = new FileOutputStream("thewing.txt")) {
//생략
} catch(IOException e){
e.printStackTrace();
}
자바에서는 실행 시 발생할 수 있는 오류(Exception & Error)를 클래스로 정의하고 있다. 예외와 에러의 상속계층도는 다음과 같다.
public class CustomException {
public static void main(String[] args) {
CustomException sample = new CustomException();
try {
sample.throwMyException(13);
} catch (MyException mye) { <<error
mye.printStackTrace();
}
}
private void throwMyException(int number) throws MyException{
try {
if(number > 12) {
throw new MyException("Number is over than 12"); <<error
}
} catch (MyException e) { <<error
e.printStackTrace();
}
}
}
예외를 어떻게 처리할지를 알아두는 것이 좋다
실행시에 발생할 확률이 매우 높은 경우에는 런타임 예외로 만드는것이 나을 수도 있다. 즉, 클래스 선언시 extends Exception 대신에 extends RuntimeException 으로 선언하는 것
이렇게 되면 해당 예외를 던지는(throw하는) 메소드를 사용하더라도 try-catch 로 묶지 않아도 컴파일시에 예외가 발생하지 않는다.
하지만, 이 경우에는 예외가 발생할 경우 해당 클래스를 호출하는 다른 클래스에서 예외를 처리하도록 구조적인 안전장치가 되어있어야만 한다.
즉, 일반적으로 실행시 예외를 처리할 수 있는 경우에는 RuntimeException 클래스를 확장하는 것을 권장한다.
임의의 예외 클래스를 만들때에는, 반드시 try-catch 로 묶어줄 필요가 있을 경우에만 Exception 클래스를 확장한다.
참조