[Java] 예외 처리

Minit88·2023년 3월 6일
0

[Java]

목록 보기
12/18
post-thumbnail

예외 처리

에러가 발생하는 원인은 수없이 다양하지만 다음의 몇 가지로 예를 들면

  • 사용자의 입력 오류
  • 네트워크 연결 끊김
  • 디스크 메모리 공간 부족 등 물리적 한계
  • 개발자의 코드 에러
  • 존재하지(유효하지) 않는 파일 불러오기

간단한 오류 예시를 예제를 통해 알아보면

public class ErrorTest {
    public static void main(String[] args) {
        BufferedReader notExist = new BufferedReader(new FileReader("없는 파일"));
        notExist.readLine();
        notExist.close();
    }
}


실제로 존재하지 않는 파일을 불러오려 시도할 때 발생하는 예외이다.

컴파일 에러

컴파일 에러는 이름 그대로 컴파일 할 때 발생하는 에러를 가리킨다. 주로 세미클론 생략, 오탈자, 잘못된 자료형, 잘못된 포맷 등 문법적인 문제를 가리키는 신택스 오류로부터 발생하기 때문에 신택스 에러라고 부른다.

public class ErrorTest {
    public static void main(String[] args) {
        int i;

        for (i= 1; i<= 5; i++ {
            System.out.println(i);
        }

    }
}


[그림] 컴파일 에러 예시

런타임 에러

런타임 시에 발생하는 에러를 런타임 에러라 칭한다. 즉, 프로그램이 실행될 때 만나게되는 에러이다.

public class RuntimeErrorTest {

    public static void main(String[] args) {
        System.out.println(4 * 4);
        System.out.println(4 / 0); // 예외 발생
    }
}

ArithtmeticException은 특정 숫자를 0으로 나웠을 때 발생하는 예외이다. 이러한 런타임 에러는 프로그램이 실행될 때에 자바 가상 머신(JVM)에 의해 감지된다.

예외 클래스의 상속 계층도


위 그림에서 보면, 자바의 모든 에러와 예외 클래스는 Throwable 클래스로부터 확장되며, 모든 예외의 최고 상위클래스는 Exception 클래스이다.
모든 예외의 최고 상위 클래스인 Exception 클래스는 다시 크게 일반 예외 클래스와 실행 예외 클래스로 나눌 수 있다.

예외 처리를 하는 법 try-catch문

자바에서 예외 처리는 try-catch 블럭을 통해 구현이 가능하다. 그 기본적인 구조는 다음과 같다.

try {
    // 예외가 발생할 가능성이 있는 코드를 삽입
} 
catch (ExceptionType1 e1) {
    // ExceptionType1 유형의 예외 발생 시 실행할 코드
} 
catch (ExceptionType2 e2) {
    // ExceptionType2 유형의 예외 발생 시 실행할 코드
} 
finally {
    // finally 블럭은 옵셔널
    // 예외 발생 여부와 상관없이 항상 실행
}

try 블럭 안에는 예외가 발생할 가능성이 있는 코드를 삽입한다. 만약에 작성한 코드가 예외 없이 정상적으로 실행되면 아래 catch은 실행되지 않고 finally 블럭이 실행된다.

catch 블럭은 예외가 발생하는 경우에 실행되는 코드로 , 여러 종류의 예외를 처리할 수 있다.
모든 예외를 받을 수 있는 Exception 클래스 하나로 처리도 가능하며, 각기 다른 종류의 예외를 하나 이상의 catch 블럭을 사용하여 처리한다.
catch 블럭이 여러개일 경우에는 일치하는 하나의 catch 블럭만이 실행되고 예외처리 코드가 종료되거나 finally 블럭으로 넘어가게 된다.

아래의 예제를 통해 자세히 알아보자

public class RuntimeExceptionTest {

    public static void main(String[] args) {

        try {
            System.out.println("[소문자 알파벳을 대문자로 출력하는 프로그램]");
            printMyName(null); // (1) 예외 발생
            printMyName("abc"); // 이 코드는 실행되지 않고 catch 문으로 이동
        } 
        catch (ArithmeticException e) {
            System.out.println("ArithmeticException 발생!"); // (2) 첫 번째 catch문
        } 
        catch (NullPointerException e) { // (3) 두 번째 catch문
            System.out.println("NullPointerException 발생!"); 
            System.out.println("e.getMessage: " + e.getMessage()); // (4) 예외 정보를 얻는 방법 - 1
            System.out.println("e.toString: " + e.toString()); // (4) 예외 정보를 얻는 방법 - 2
            e.printStackTrace(); // (4) 예외 정보를 얻는 방법 - 3
        } 
        finally {
            System.out.println("[프로그램 종료]"); // (5) finally문
        }
    }

    static void printMyName(String str) {
        String upperCaseAlphabet = str.toUpperCase();
        System.out.println(upperCaseAlphabet);
    }
}

printMyName() 메서드는 String 타입의 데이터를 받아 문자열을 대문자로 변환후 출력하는 메소드이다.

main 클래스의 try 문부터 순서대로 printMyName(null) 값이 들어가게 되어 NullPointerException이 발생하게 된다. 즉, 바로 밑의 printMyName("abc")는 실행이 안되고 catch (NullPointerException e) 블록이 실행되게 된다.

이후 catch 블록이 종료가 되면 finally 블록으로 이동해 해당 문자열을 출력 후 코드는 종료가 된다.

Process finished with exit code 0 는 프로그램이 정상적으로 실행되었다는 뜻으로 예외처리를 통해 프로그램이 정상 실행되었다.

예외 전가

앞서 배운 try-catch 문 외에 예외를 호출한 곳으로 다시 예외를 떠넘기는 방법도 있다.
이를 위해서는 메서드의 선언부 끝에 아래와 같이 throws 키워드와 발생할 수 있는 예외들을 쉼표로 구분하여 나열해주면 된다.

반환타입 메서드명(매개변수, ...) throws 예외클래스1, 예외클래스2, ... {
	...생략...
}

특정 메서드에서 모든 종류의 예외가 발생할 가능성이 있는 경우 아래와 같이 작성할 수 있다.

void ExampleMethod() throws Exception {
}

Exception클래스는 모든 예외의 상위 클래스이기 때문에, 그 하위 클래스 타입의 예외 클래스들이 모두 포함되어진다.

아래의 예제를 보면

public class ThrowExceptionTest {

    public static void main(String[] args) {
        try {
            throwException();
        } catch (ClassNotFoundException e) {
            System.out.println(e.getMessage());
        }
    }

    static void throwException() throws ClassNotFoundException, NullPointerException {
        Class.forName("java.lang.StringX");
    }


해당 예외 처리는 throws를 선언해 해당 예외를 발생한 메서드 안에서 처리하지 않고 메서드를 호출한 곳으로 다시 떠넘기고 있다.
예외처리 책임은 main메서드가 지게 되었다.

예외를 의도적으로 발생시키기

public class ExceptionTest {

    public static void main(String[] args) {
        try {
            Exception intendedException = new Exception("의도된 예외 만들기");
            throw intendedException;
        } catch (Exception e) {
            System.out.println("고의로 예외 발생시키기 성공!");
        }
    }
    
}

//출력값
고의로 예외 발생시키기 성공!

위 코드에서 throw를 이용해 의도적으로 예외를 발생시켜 catch 구문을 실행함을 볼 수 있다.

profile
" To be BE "

0개의 댓글