<Java> 예외 처리

라모스·2021년 9월 2일
0

Java☕

목록 보기
9/14
post-thumbnail

" 자바의 예외 처리에 대해 학습하세요. "

학습할 것

  • 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
  • 자바가 제공하는 예외 계층 구조
  • Exception과 Error의 차이는?
  • RuntimeException과 RE가 아닌 것의 차이는?
  • 커스텀한 예외 만드는 방법

예외(Exception)?

문법에 맞지 않게 코드를 작성하고 컴파일하면, 자바 컴파일러는 문법 오류(Syntax Error)를 발생시킨다. 또한 문법에는 맞더라도 프로그램 실행 시 예상치 못한 오류가 발생하기도 한다. 오류(error)는 시스템 레벨에서 프로그램에 심각한 문제를 야기하여 실행 중인 프로그램을 종료시키는데, 이러한 오류는 개발자가 미리 예측하여 처리할 수 없는 것이 대부분이라 이에 대한 처리는 할 수 없다.

반면, 예외(Exception)는 발생할 수 있는 상황을 미리 예측하여 처리하는 것이 가능하다.

예외 상황의 예를 들면 다음과 같다.

  • 정수를 0으로 나누는 경우
  • 배열보다 큰 인덱스로 배열의 원소를 접근하는 경우
  • 존재하지 않는 파일을 읽으려고 하는 경우
  • 정수 입력을 기다리는 코드가 실행되고 있을 때, 문자가 입력된 경우

1. 자바에서 예외 처리 방법(try, catch, throw, throws, finally)

예외가 발생할 때 대응하기 위해서는 try-catch-finally 문을 사용한다. finally 블록은 생략 가능하다.

try {
    // 예외가 발생할 가능성이 있는 실행문(try 블록)
} catch (처리할 예외 타입 선언) { // 예외클래스이름 객체변수
    // 예외 처리문(catch 블록)
} finally {
    // 예외 발생 여부와 상관없이 무조건 실행되는 문장
    // (finally 블록)
}

try 블록에는 여러 개의 catch 블록이 올 수 있고, 이 중 발생한 예외의 종류와 일치하는 단 한개의 catch 블록만 수행된다. 다음 예시 코드를 살펴보자.

import java.util.Scanner;
public class DevideByZeroHandling {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(true) {
            int dividend = sc.nextInt();
            int divisor = sc.nextInt();
            try {
                System.out.println("결과: " + dividend/divisor);
                break;
            }
            catch(ArithmeticException e) {
                e.printStackTrace();
                // e.getMessage();
            }
        }
        sc.close();
    }
}

정수를 0으로 나누는 경우에 발생하는 예외처리를 작성한 코드이다.
catch 블록을 보면 ArithmeticException e 처럼 예외 클래스의 인스턴스를 만들어 해당 클래스 내에 있는 예외 발생 시 에러를 출력하는 함수를 사용한다.

  • printStackTrace(): 예외 발생 당시의 호출 스택에 있었던 메소드 정보와 예외 메시지를 화면에 출력한다.
  • getMessage(): 발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.

자주 발생하는 예외는 다음과 같다.

예외 처리 메커니즘

예외 처리 메커니즘은 다음과 같다.

1번 try 블록에서 예외가 발생하지 않고, 바깥쪽 try 블록에서도 예외가 발생하지 않는다면, 6번 finally 블록이 바로 실행된다.

하지만, 1번 try 블록에서 예외가 발생하면, 2,3번 catch 블록에서 해당 예외를 처리할 수 있는지 검사하게되고, 만약 적절한 catch 블록을 찾지 못하면 try 블록의 4,5번 catch 블록도 차례대로 검사하게 된다. 이 때 해당 예외를 처리할 수 있는 catch 블록을 찾게되면, 해당 catch 블록을 실행 한 뒤 6번 finally 블록을 실행한다.

하지만 모든 catch 블록이 해당 예외를 처리할 수 없으면, 예외는 처리되지 못한 채 해당 프로그램은 강제로 종료된다.

Multicatch block

다음 코드와 같이 Java 7부터 여러 catch block을 하나로 합칠 수 있게 되었다.

public class ExceptionExample {
    public static void main(String[] args) {
        try {
            System.out.println(1 / 0);
        } catch(IllegalArgumentException | ArithmeticException e) {
            e.printStackTrace(); // 나열된 예외 클래스들이 상속관계에 있다면 오류 발생
        }
    }
}

다만, 나열된 예외 클래스들이 서로 상속관계에 있다면 컴파일러는 중복된 코드를 제거하라는 의미로 에러를 발생시킨다.

throw

throw 키워드를 사용하여 강제로 예외를 발생시키는 것도 가능하다.
예를 들어, 사용자가 욕설을 사용하는 닉네임을 사용하지 못하게 하고싶다면 다음과 같이 예외를 발생시켜 프로그램을 중단시키면 된다.

public class ExceptionExample {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("아이디를 입력하세요");
        String userName = sc.nextLine();

        try {
            if (userName.equals("(욕)")) {
                throw new IllegalArgumentException("부적절한 이름입니다.");
            }
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }
    }
}

throws

메소드 선언부에 throws 키워드를 사용하여 해당 메소드를 사용할 때 발생할 수 있는 예외를 미리 명시할 수 있다. 이렇게 하면 해당 메소드를 사용할 때 발생할 수 있는 예외를 사용자가 충분히 인지할 수 있으며, 그에 대한 처리까지 강제할 수 있다.

void function() throws Exception1, Exception2, ... {
    // 메소드 내용
}
public class ExceptionExample {
    static void handlingException() throws Exception { throw new Exception(); }
    public static void main(String[] args) {
        try {
            handlingException();
        } catch (Exception e) {
            System.out.println("main() 메소드에서 예외가 처리됨!");
        }
    }
}

2. 자바가 제공하는 예외 계층 구조

자바는 실행 시 발생할 수 있는 오류(Exception, Error)를 클래스로 정의하고 있다.
예외와 에러의 상속 계층 구조는 다음과 같다.

Exception과 Error는 Throwable이라는 클래스를 상속받고 있으며 Throwable은 Object를 직접 상속받고 있다.

3. RuntimeException과 RE가 아닌 것의 차이는?

Exception을 상속받는 클래스들 중에서 RuntimeException을 제외하곤 모두 Checked Exception에 속한다.

Checked Exception 이란 컴파일 시점에서 확인될 수 있는 예외이다. 만약 코드 내에서 이를 발생시킨다면, 해당 예외는 반드시 처리되거나, 해당 코드가 속한 메서드의 선언부에 예외를 선언해주어야 한다.

public class ExceptionExample {
    public static void main(String[] args) {
        methodA();
    }
    static void methodA() throws IOException {
        throw new IOException();
    }
}

throws로 선언부에 예외를 선언하지 않는다면 컴파일 자체가 안된다.

반면, Unchecked Exception은 컴파일 단계에서 확인되지 않는 예외이다.
Java에선 RuntimeException과 그 하위 클래스, Error와 그 하위 클래스가 이에 속한다.
이 예외들은 컴파일러가 예외를 처리하거나 선언하도록 강제하지 않기 때문에 개발자가 알아서 처리를 해야한다.

예외를 두 타입으로 나눈 이유는 오라클 공식 문서를 통해 확인할 수 있다.

4. 커스텀한 예외 만드는 방법

Exception 클래스를 상속받아 새롭게 커스터마이징한 예외 클래스를 정의하여 사용하는 것도 가능하다. 사용자 정의 예외 클래스는 생성자, 필드 및 메소드도 원하는 만큼 추가할 수 있다.

class MyException extends RuntimeException {
    MyException(String errMsg) {
        super(errMsg);
    }
}

References

  • Java의 정석 3판
  • 명품 JAVA Programming
  • TCP School, Java
profile
Step by step goes a long way.

0개의 댓글