📌18-1. 자바 예외처리의 기본

프로그램에 접근할 수 있는 사람은 프로그래머와 사용자가 있다.
프로그래머가 사용할 때에도 오류가 발생할 수도 있고, 사용자가 사용할 때에도 잘못 사용함으로써 오류가 발생할 수도 있다.

지금부터 다루게 되는 예외는 일반적으로 사용자가 잘못 사용하여 발생하는 오류들을 말하게 된다.

그리고 프로그래머는 일반적이지 않은 상황, 즉 프로그래머가 기대하지 못한 상황에서도 프로그램이 동작할 수 있도록 프로그래밍을 해야 하고 이를 예외처리라고 한다.

예외 상황을 알리기 위해 존재하는 클래스가 있다.

  • java.lang.ArithmeticException : 수학 연산에서의 오류 상황을 의미하는 예외 클래스
  • java.util.InputMismatchException : Scanner 클래스를 통한 값의 입력에서의 오류 상황을 의미하는 예외 클래스

예외 상황이 발생하면 JVM이 이 클래스의 인스턴스를 만들어 알려준다.

✅try~catch 문

별도의 예외처리 방법이 없었던 때에는 if~else 문을 통해서 처리할 수 있었으나 이는 전체 코드를 볼 때, 흐름을 한눈에 파악하기 힘들다.

따라서, 예외처리 구문을 따로 구분하기를 원했고 이것이 try~catch 문이다.

try {
    // 관찰 영역
}
catch {
    // 처리 영역

}

하나의 catch 문이 전체 영역의 예외 상황을 관리할 수 없기 때문에 catch 문이 담당할 영역을 try 문으로 감싸준다.

예외처리의 흐름을 살펴보자.

  1. 예외 상황 발생
  2. JVM의 예외 클래스 인스턴스 생성
  3. try 문 존재 여부 확인
  4. catch 문에 인스턴스 전달
  5. 예외처리 끝

예외처리가 끝나면 해당 try~catch 문을 모두 건너 뛰게 된다.
또한, JVM이 try 문에 진입하면 속도가 느려지기 때문에 너무 많은 코드를 try 문으로 감싸는 것은 비효율적이다.

따라서, try 문으로 감쌀 영역을 결정해야 한다.

"여기서 예외가 발생하면 나머지 일들은 무효화될 필요가 있다, 건너 뛸 필요가 있다" 하는 것들을 하나의 작업으로 간주하고 try 문으로 하나로 묶어준다.

하나의 try 문에서 둘 이상의 예외가 발생할 수도 있다.

// 방법 1

try {
    ...
}
catch(ArithmeticException e) {
    ...
}
catch(InputMismatchException e) {
    ...
}

// 방법 2

try {
    ...
}
catch(ArithmeticException | InputMismatchException e) {
    ...
}

✅Throwable 클래스

java.lang.Trowable 클래스는 모든 예외 클래스의 최상위 클래스이다. 물론 이 클래스도 Object 클래스를 상속한다.

Trowable 클래스에는 대표적인 두 개의 메소드가 있다.

  • public String getMessage() : 예외의 원인을 담고 있는 문자열을 반환
  • public void printStackTrace() : 예외가 발생한 위치와 호출된 메소드의 정보를 출력, 원인도 반환하기 때문에 보통 둘 중 하나만 사용한다.

예외 발생 지점에서 예외를 처리하지 않으면 해당 메소드를 호출한 영역으로 예외가 전달된다.

위 코드에서 예외가 발생할 수 있는 곳은 md2 메소드이다. 그러나 try~catch 문이 없어 예외를 처리할 수 없다.
따라서 md1 메소드에서 예외를 처리해야 하는데 역시 try~catch 문이 없기 때문에 main 메소드로 책임이 넘어간다.
그러나 main 메소드에서도 예외를 처리할 수 없기 때문에 JVM으로 그 책임이 넘어간다.
JVM에서는 예외 클래스의 인스턴스의 printStackTrace()를 호출하고 종료시키게 된다.

✅이외의 예외 클래스

  • ArrayIndexOutOfBoundException
  • ClassCastException
  • NullPointerException

📌18-2. 예외처리에 대한 나머지 설명들

✅예외 클래스의 구분

여러 예외 클래스들을 다음 세가지로 나눌 수 있다.

  • Error 클래스를 상속하는 예외 클래스
  • Exception 클래스를 상속하는 예외 클래스
  • RuntimeException 클래스를 상속하는 예외 클래스
    : RuntimeException 클래스는 Exception 클래스를 상속한다.

Error 클래스를 상속하는 예외 클래스의 예외 상황은 시스템 오류 수준의 예외 상황으로 프로그램 내에서, 코드 레벨로 처리할 수 있는 수준의 예외가 아니다.

RuntimeException 클래스를 상속하는 예외 클래스의 예외 상황은 코드 오류로 발생하는 경우가 대부분이다. 따라서 이 유형의 예외 발생시에는 코드의 수정을 고려해야 한다.

✅Exception 클래스

우리가 진정 예외라고 할 수 있는 상황들은 Exception 클래스로 정의되어 있다. 따라서 예외의 처리를 어떻게 할 것인지 반드시 명시해 주어야 한다. 그렇지 않으면 컴파일 오류로 이어진다.

이때의 예외처리에는 두 가지 방법이 있다.

  1. try~catch 문
  2. throws

앞에서 RuntimeException 클래스의 경우에는 우리가 명시하지 않아도 try~catch 문이 없으면 호출한 메소드로 그 책임이 알아서 넘어갔지만, Exception 클래스는 우리가 명시해줘야 한다.

✅프로그래머 정의 예외 클래스

문법적 예외가 아닌 논리적 예외를 고려해서 프로그래머가 예외 클래스를 만들 수도 있다.

class AAA extends Exception {...}

Exception 클래스를 상속하는 것이 예외 클래스의 유일한 조건이다.

age에 0보다 작은 값이 들어와 if 문의 throw를 만나게 되면, JVM은 예외처리 메카니즘을 실행하게 된다. 또한, 우리가 JVM 대신에 예외 클래스의 인스턴스를 만들어줘야 한다.

throwsthrow를 구분해야 한다.

Exception 클래스를 간접 상속하여도 예외 클래스가 된다.
이를 이용해서 다음의 예외 클래스를 만들었다고 생각해보자.

class FirstException extends Exception {...}
class SecondException extends FirstException {...}
class ThirdException extends SecondException {...}

상속의 개념을 통해 FirstException 예외 클래스로 만든 참조 변수로는 세 가지의 예외 클래스를 모두 참조할 수 있다.

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

그러나, catch 문을 이렇게 구성하면 첫번째 catch 문이 모든 예외 상황을 받을 수 있기 때문에 나머지 두 개의 catch 문은 기회를 받지 못한다.

다행히 컴파일러는 이 부분에 대해 컴파일 에러를 전달해 준다.

이를 해결하기 위해서는 catch 문의 순서를 바꿔야 한다.

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

✅finally

코드의 실행이 try 문으로 진입하면, 무조건 finally 문을 실행하게 된다.

// 작성법 1
try {...}
finally {...}

// 작성법 2
try {...}
catch(...) {...}
finally {...}

예를 들어 파일의 입출력의 경우, 파일을 한번 열었으면 반드시 닫아야 한다. 그러한 이유로 finally 문 안에 파일을 닫는 코드를 넣었다고 해보자.

그러나 닫는 과정에서도 예외 상황이 발생할 수 있기 때문에, 이를 위해서 또다시 try~catch 문이 등장했다. 이렇게 되면 코드가 복잡해지고 보기 불편해진다.

이를 해결하기 위해 나온 것이 try-with-resources이다.

파일을 포함한 자원은 open-close의 개념이 존재하기 때문에 자원을 제어하기 위한 코드와 try의 조합을 만들어냈다.

writer가 참조하는 인스턴스가 AutoCloseable 인터페이스를 구현하면 try-with-resources를 사용할 수 있다.


🔗강의 및 자료 출처

네이버 카페 || 윤성우의 프로그래밍 스터디그룹

profile
Hello World!

0개의 댓글

Powered by GraphCDN, the GraphQL CDN