티스토리 뷰

자바 예외와 예외 클래스

에러와 예외는 다르다. 에러(ERROR)는 JVM실행에 문제가 생겼다는 것으로 JVM 위에서 실행되는 프로그램을 아무리 잘 만들어도 결국 실행이 불가능하다. 이것은 개발자가 대처 가능한 문제가 아니다.

반면에 예외는 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류를 말한다.
예외가 발생되면 프로그램이 곧바로 중단되는 점에서는 에러와 동일하지만 예외는 예외처리(Exception Handling) 을 통해 프로그램을 종료하지 않고 실행상태를 유지시킬 수 있다.

예외에는 일반예외와 실행예외가 존재한다.

일반예외는 컴파일러 체크 예외라고도 한다. 자바소스를 컴파일 하는 과정에서 예외처리 코드가 필요한지 검사한다. 만약 예외처리 코드가 없다면 컴파일 오류가 발생한다.

실행예외는 컴파일하는 과정에서 예외처리 코드를 검사하지 않는다. 자바에서는 예외를 클래스로 관리한다. JVM은 프로그램을 실행하는 도중 예외가 발생하면 해당 예외 클래스로 객체를 생성하고 이후 예외처리 코드에서 해당 객체를 이용할 수 있도록 해준다.

실행예외 종류

실행예외는 자바 컴파일러가 체크를 하지 않기 때문에 오로지 개발자의 경험에 의해 예외처리 코드를 삽입해야 한다. 만약 개발자가 예외처리 코드를 넣지 않았을 경우 해당 예외가 발생하면 프로그램은 곧바로 종료된다.

다음은 자바 프로그램에서 자주 발생되는 실행 예외이다.

NullPointerException

이것은 객체 참조가 없는 상태, 즉 null값을 갖는 참조 변수로 객체 접근연산자인 도트(.)를 사용했을 때 발생한다.
객체가 없는 상태에서 객체를 사용하려 했기에 발생하는 예외이다.

ArrayIndexOutOfBoundsException

배열에서 인덱스 범위를 초과하여 사용할 경우 발생하는 예외이다.
예를들어 int[] arr = new int[3] 과 같이 총 0~2 인덱스를 가지는 배열을 선언했지만 arr[3] 으로 접근하면 해당 예외가 발생한다.

NumberFormatException

문자열로 되어있는 데이터를 숫자로 변경하는 경우가 자주 발생한다. 그 방법은 여러가지가 있지만 가장 많이 사용되는 코드는 Integer.parseInt(String s) 일것이다. 하지만 만약 String s 에 숫자로 변환될 수 없는 문자가 포함되어 있다면 해당 예외가 발생한다.

ClassCastException

캐스팅은 상위 클래스와 하위클래스, 인터페이스와 구현클래스 간에 발생한다. 만약 이러한 관계가 아니라면 클래스는 다른 클래스로 캐스팅할 수 없다. 하지만 만약 이러한 관계가 아님에도 강제로 타입을 변환하려 한다면 해당 예외가 발생한다.

예외처리 코드

위와 같은 예외가 발생했을 때 개발자가 적절하게 예외처리 코드를 사용하지 않는다면 실행 도중에 프로그램이 종료되는 일이 발생할 수 있다. 예외처리는 try-catch-finally 블록을 이용하여 처리한다.

try블록에는 예외발생이 가능한 코드를 작성한다. 그리고 catch블록에는 만약 예외가 발생했을 때 처리되어야 할 코드를 작성한다. 그리고 finally블록에는 예외 발생의 유무에 상관없이 항상 실행되어야 하는 코드를 작성한다. finally 코드는 생략이 가능하다.

try {
    int value = Integer.parseInt("100a");
} catch(NumberFormatException e) {
    System.out.println("숫자 변환 예외 발생");
} finally {
    System.out.println("다시 실행");
}

만약 try블록 내에서 다양한 예외가 발생하면 어떻게 할까?

첫번째 방법은 catch블록을 여러개 작성하는 것이다. 발생할 수 있는 모든 예외에 대해 catch블록을 작성하고 예외가 발생했을 때 실행될 코드를 작성하면 된다.
다중 catch블록을 작성할 때 주의해야 할 점은 상위 예외 클래스가 하위 예외클래스보다 아래쪽에 위치해야 한다는 점이다. 모든 예외클래스는 Exception 클래스를 상속하게 되므로 만약 이 클래스를 가장 위쪽에 위치시킨다면 가장 먼저 이 catch블록이 실행되고 그 이후 catch블록은 실행되지 않기 때문이다.

두번째 방법은 멀티 catch을 사용하는 것이다. 자바7부터 가능한 방법으로 catch의 괄호 내에 처리하고 싶은 예외를 | 로 연결하면 된다.

catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {}

추가적으로 try-with-resources 를 사용하면 예외 발생여부와 상관 없이 사용했던 리소스 객체(입출력 스트림, 소켓, 채널 등)의 close() 메소드를 호출해서 안전하게 리소스를 닫아준다.
만약 try-catch-finally 를 사용한다면 finally블록에서 각종 리소스를 집적 닫아줘야하는데 위 블록을 사용하면 조금 더 편리하게 코드를 작성할 수 있다.
만약 try-with-resources 를 사용하고자 한다면 리소스 객체는 java.lang.AutoCloseable 인터페이스를 구현하고 있어야 한다.

예외 떠넘기기

메소드 내부에서 예외가 발생하면 try-catch 를 통해 예외를 처리하는 것이 일반적이지만 경우에 따라 메소드를 호출한 곳으로 예외를 떠넘길 수도 있다. 이때는 throws 라는 키워드가 사용된다.

키워드 이름과 같이 만약 어떠한 메소드에서 예외가 발생했을 때, 그 메소드의 구현부에서 예외를 처리하는 것이 아닌 메소드를 호출한 쪽으로 예외를 넘겨 그쪽에서 예외를 처리하게 하는 것이다.
메소드를 작성할 때 throws 키워드를 붙여서 메소드를 작성해야하며 해당 메소드는 반드시 try 블록 내부에서 실행되어야 한다.

public void method() throws Exception {}
// or
public void method() throws ClassNotFoundException {} //구체적인 예외클래스도 가능

사용자 정의 예외

위에서 자바 내에서 기본적으로 제공하는 예외 객체들을 살펴보았는데 실제 프로그램을 작성하다보면 기본적인 예외 객체 외에 추가적인 예외 객체가 필요한 경우가 있다. 이럴때는 개발자가 집적 예외 객체를 만들어야 한다.

사용자 정의 예외는 컴파일러가 체크하는 일반예외로도 만들 수 있고, 컴파일러가 체크하지 않는 실행예외로도 만들 수 있다. 일반예외로 만들 경우에는 Exception 을 상속하면 되고 실행예외로 만들때는 RuntimeException 을 상속하면 된다.

public class ExampleException extends RuntimeException {}

만약 예외를 우리의 코드에서 집적 발생시켜야 하는 경우가 있을 수 있다.
그럴때는 집적 예외 객체를 생성하고, 해당 예외를 throw하면 된다.
throw new ExampleException(); 과 같이 작성하면 해당 코드에서 예외가 발생하게 된다.

public void method(int number) throws ExampleException {
    if (number < 0) {
        throw new ExampleException();
    }
} 

이렇게 사용자가 코드를 작성하고, 예외를 발생시키면 해당 메소드를 실행한 곳에서는 try-catch 블록을 통해 해당 예외를 잡고 처리해야 한다.

예외를 처리할때는 예외객체 내부에 있는 메소드를 사용하여 예외정보를 확인할 수 있다.
가장 많이 사용되는 정보는 getMessage()printStackTrace() 이다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함