자바의 스택 추적
예외를 잡지 못해 프로그램이 실패하면 자바 시스템은 그 예외의 스택 추적 정보를 자동으로 출력한다. 스택 추적은 예외 객체의 toString 메서드를 호출해 얻는 문자열로, 보통은 예외의 클래스 이름 위에 상세 메세지가 붙은 형태다. 따라서 toString 메서드에 실패 원인에 관한 정보를 가능한 한 많이 담아 반환하는 일은 아주 중요하다. 에러를 담당하는 Throwable 클래스의 생성자와 toString()메서드는 다음과 같이 되어 있다.
public Throwable(Throwable cause) {
fillInStackTrace();
detailMessage = (cause==null ? null : cause.toString());
this.cause = cause;
}
public String toString() {
String s = getClass().getName();
String message = getLocalizedMessage();
return (message != null) ? (s + ": " + message) : s;
}
예를 들어 ArrayIndexOutOfBoundException을 이용하여 예외를 던지면 내부적으로 super()를 호출하여 Throwable에 전달된다.
public class ExceptionTemp {
public static void main(String[] args) {
throw new ArrayIndexOutOfBoundsException();
}
}
이 경우에는 인자로 어떠한 정보도 주지 않았기 때문에 message정보가 없어 다음과 같이 출력된다.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
at ch10.ExceptionTemp.main(ExceptionTemp.java:6)
실패 순간을 포착하려면 발생한 예외에 관여된 모든 매개변수와 필드의 값을 실패 메세지에 담는게 좋다. (보안과 관련된 정보는 주의해서 다루자.)
예외 생성자와 상세 메세지 생성
실패를 적절히 포착하려면 필요한 정보를 예외 생성자에서 모두 받아서 상세 메세지까지 미리 생성해놓는 방법도 괜찮다. 예를 들어 현재 IndexOutOfBoundsException 생성자는 String을 받지만, 다음과 같이 구현해도 좋다
public IndexOutOfBoundsException(int lowerBound, int upperBound, int index) {
super(String.format("최솟값: %d, 최댓값: %d, 인덱스: %d", lowerBound, upperBound, index));
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.index = index;
}
이렇게 작성하면 프로그래머가 던지는 예외는 자연스럽게 실패를 더 잘 포착한다. 또한 고품질의 상세 메세지를 만들어내는 코드를 예외 클래스 안으로 모아주는 효과도 있어, 클래스 사용자가 메세지를 만드는 작업을 중복하지 않아도 된다.