반응형

자바의 예외처리 관련 내용이 의외로 적지 않습니다.

따라서 예외처리를 하지 않는 상황에서는 불필요한 내용이라 생각하고 넘어가고 싶을 수도 있지만,

자바에서는 예외처리가 차지하는 부분이 크기 때문에 마지막까지 이해하고 넘어가야 합니다.

 

예외 상황을 알리기 위해 정의된 클래스의 종류

package ExceptionHandling;

public class ArrayIndexOutOfBounds {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        for (int i = 0; i < 4; i++) {
            System.out.println(arr[i]);
        }
    }
}

package ExceptionHandling;

class Board {
}

class PBoard extends Board {
}

public class ClassCast {
    public static void main(String[] args) {
        Board pboard1 = new PBoard();
        PBoard pBoard2 = (PBoard) pboard1;

        System.out.println("-----");

        Board board1 = new Board();
        PBoard board2 = (PBoard) board1;
    }
}

package ExceptionHandling;

public class NullPointer {
    public static void main(String[] args) {
        String str = null;
        System.out.println(str);
        int length = str.length();
    }
}


예외 클래스의 구분

예외 클래스의 최상위 클래스는 Throwable 입니다.

그런데 이를 상속하는 예외 클래스는 다음과 같이 세 부류로 나뉩니다.

  1. Error 클래스를 상속하는 예외 클래스
  2. RuntimeException 클래스를 상속하는 예외 클래스 -> RuntimeException 클래스는 Exception 클래스를 상속합니다.
  3. Exception 클래스를 상속하는 예외 클래스

1.  Error 클래스를 상속하는 예외 클래스

예외 클래스의 예와 그 발생 상황을 정리하면 다음과 같습니다.(객체명에 Error가 포함됩니다.)

  • VirtualMachineError -> 가상머신에 심각한 오류 발생
  • IOError -> 입출력 관련해서 코드 수준 복구가 불가능한 오류 발생

VirtualMachineError의 구체적인 예

VirtualMachineError 클래스를 상속하는 예외 클래스로 OutOfMemoryError가 있습니다.

이는 프로그램의 실행에 필요한 메모리 공간이 부족한 상황에서 발생하는 예외입니다.

따라서 이 예외가 발생하면 메모리를 비효율적으로 또는 부적절하게 사용하는 부분의 코드를 수정해야 합니다.

 

IOError의 구체적인 예

자바 프로그램이 임의의 파일에 저장된 데이터를 읽는 중에 갑자기 하드디스크에 물리적 오류가 발생하여 더 이상 파일에 저장된 데이터를 읽을 수 없는 상황이 생길 수 있습니다.

그리고 이런 상항에서 발생하는 것이 IOError Exception 입니다.

즉 Error 클래스를 상속하는 예외는 처리의 대상이 아닙니다.

바꿔 말하면 처리할 수 있는 예외가 아닙니다. 따라서 이런 유형의 예외가 발생하면 그냥 프로그램이 종료되도록 놔두고 이후에 원인을 파악하는 과정이 필요합니다.

 

2. RuntimeException 클래스를 상속하는 예외 클래스

앞서 보였던 예외 클래스가 이에 해당합니다.

  • ArithmeticException
  • ClassCastException
  • IndexOutOfBoundsException
  • NegativeArraySizeException -> 배열 생성시 길이를 음수로 지정하는 예외 발생
  • NullPointerException
  • ArrayStoreException -> 배열에 적절치 않는 인스턴스를 저장하는 예외 발생

3. Exception 클래스를 상속하는 예외 클래스

마지막으로 Exception 클래스를 상속하는 예외 클래스가 있습니다.

RuntimeException 클래스를 직접 혹은 간접적으로 상속하지 않고 Exception 클래스만을 상속하는 예외클래스가 해당됩니다.

그 수로만 따지면 나머지 둘에 비해 가장 많습니다.

그리고 이들은 특정 클래스 또는 메소드에 연관되어 있어서 나열하여 설명하는 것은 의미가 없습니다.

앞으로 공부하면서 이 부류에 속한 예외 클래스를 하나씩 접하게 됩니다.

Exception을 상속하는 예외 클래스 중에서 비교적 빨리 접하게 될 클래스는 다음과 같습니다.

java.io.IOException


프로그래머가 정의하는 예외

지금까지 소개한 예외 클래스는 모두 자바에서 정의한 클래스였습니다.

그러나 프로그래머가 직접 예외 클래스를 정의하고 이를 기반으로 특정 상황에서 예외가 발생하도록 할 수 있습니다.

프로그래머가 정의하는 예외 클래스의 예의 핵심은 Exception 을 상속하는데 있습니다.

Exception 클래스를 아래의 코드로 구현해보겠습니다.

package ExceptionHandling;

public class ReadAgeException extends Exception {
    public ReadAgeException() {
        super("유효하지 않은 나이가 입력되었습니다.");
    }
}

위의 클래스는 Exception 을 상속하는 점을 제외하면 일반 클래스와 차이가 없습니다.

생성자에서는 상위 클래스의 생성자를 호출하면서 예외 상황에 대한 설명을 담고 있는 문자열을 전달하는데,

이 문자열은 앞서 보였던 Throwable 클래스에 정의된 public String getMessage() 메소드 호출 시 반환 됩니다.

 

그럼 우리가 정의한 ReadAgeException 예외 클래스를 대상으로 예외를 발생시키고 이를 처리해보겠습니다.

package ExceptionHandling;

import java.util.Scanner;

public class MyExceptionClass {
    public static void main(String[] args) {
        System.out.println("나이 입력 : ");

        try {
            int age = readAge();
            System.out.printf("입력된 나이 : %d", age);
        } catch (ReadAgeException e) {
            System.out.println(e.getMessage());
        }
    }

    public static int readAge() throws ReadAgeException {
        Scanner scanner = new Scanner(System.in);
        int age = scanner.nextInt();
        if (age < 0) {
            throw (new ReadAgeException());
        }
        return age;
    }
}

정수를 입력받았는데 그 수가 음수인 것은 문법적으로는 오류가 아닙니다.

하지만 프로그램 특성상 사람의 나이는 음수가 될 수 없으므로 오류가 맞습니다.

이러한 상황을 예외로 처리하기 위해서 예외 클래스를 직접 정의하였습니다.

이렇듯 예외 클래스의 인스턴스를 생성하고, 이를 대상으로 throw 선언을 하면 예외가 발생합니다.

물론 이렇게 발생한 예외도 Exception 을 상속하는 예외이므로 try ~ catch 문으로 처리를 하거나 throws 선언을 통해 넘겨야 합니다.


잘못된 catch 구문의 구성

다음과 같이 세 개의 예외 클래스가 정의되었다고 가정합니다.

class FirstException extends Exception {
}

class SecondException extends FirstException {
}

class ThirdException extends SecondException {
}

그리고 위의 세 종류 예외가 모두 발생 가능한 영역에 다음과 같이 try ~ catch 문을 구성하였다고 가정합니다.

 try {
            ...
        } catch (FirstException e) {

        } catch (SecondException e) {

        } catch (ThirdException e) {
            
        }

예외처리의 내용만 놓고 보면 문제가 없어보이지만 컴파일 오류가 발생합니다.

그리고 그 오류의 내용은 다음과 같습니다.

"두 번째, 세 번째 catch 구문은 실행될 일이 절대 없습니다."

그 이유는 예외 클래스 Second, Third 가 직접 혹은 간접적으로 FirstException 을 상속하고 있기 때문입니다.

그래서 catch (FirstException e) {} 구문에서 모든 예외 인스턴스를 처리할 수 있기 때문에 컴파일 오류가 발생합니다.

따라서 위와 같이 catch 문을 구성하고자 한다면 다음과 같이 순서를 변경해야 합니다.

try {
            ...
        } catch (ThirdException e) {

        } catch (SecondException e) {

        } catch (FirstException e) {
            
        }

finally 구문

try ~ catch 문은 하나의 문장이므로 try 구문 홀로 존재할 수 없습니다.

반드시 catch 구문이 하나 이상 등장해야 합니다.

그런데 try 에 이어서 다음과 같이 finally 구문을 둘 수도 있습니다.

반응형

'JAVA > 기본 개념' 카테고리의 다른 글

#5 추상 클래스 (Abstract Class)  (0) 2022.08.24
#8 Object 클래스  (0) 2022.08.23
#3 인터페이스  (0) 2022.08.22
#1 자바 개발 핵심 원칙  (0) 2022.07.11
[Java] 컴파일(Compile)의 이해  (0) 2020.11.04

+ Recent posts