본문 바로가기
언어 공부/Java

[Java] Java 예외 처리

by 희조당 2022. 9. 20.
728x90

✍️ 학습할 것

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

📌 자바에서 예외 처리 (try, catch, throw, throws, finally)

프로그램 실행 중에 어떤 문제 때문에 오작동하거나 비정상적으로 종료되는 경우를 오류(Error)라고 한다.

보통 오류는 다음과 같이 3가지로 구분할 수 있다.

  • 컴파일 에러 : 컴파일 시 발생하는 오류
  • 런타임 에러 : 실행 도중에 발생하는 오류
  • 논리 에러 : 실행은 되지만 의도치 않은 동작을 유발하는 오류

컴파일러를 통해 문제없이 컴파일을 마친다고 해서 런타임에 발생하는 잠재적인 오류까지 잡아낼 수 없다.

이렇게 런타임에 발생하는 오류를 자바에서는 예외(Exception)와 오류(Error)로 구분해서 대응한다.

✔️ 예외 처리 (Exception Handling)

예외 처리란? 런타임에 발생하는 예기치 못한 예외를 대비하는 것이다.

발생한 예외를 처리하지 못하면 프로그램이 종료되며 해당 예외를 JVM의 예외 처리기가 받아 예외의 원인을 출력한다.

✔️ try - catch - finally

예외를 처리하기 위해서 try-catch 문을 사용한다. try-catch 문의 특징은 다음과 같다.

// Example
try {
   // 예외가 발생할 수 있는 코드
   System.out.println("try문 실행");
   System.out.println(0/0);
   // 예외가 발생하면 다음 문장들은 실행되지 않는다.
   System.out.println("실행되지 않는다!!");
} catch (ArithmeticException arithmeticException) {
   System.out.println("catch문 실행");
   System.out.println("ArithmeticException 예외 발생");
} catch (Exception exception) {
   // 하나의 try에 여러 개의 catch를 사용할 수 있다.
   try {
      System.out.println("catch문 안에 try문");
   } catch (Exception exception1) {
      // 중복된 try-catch문의 참조변수는 중복 x
   }
} finally {
   // 예외 발생과 상관없이 무조건 실행되는 코드
   System.out.println("finally문 실행");
}
  • 하나의 try에 여러 개의 catch를 사용할 수 있다. if-else if-else 문처럼 단 하나의 블록만 실행된다.   
  • 만약에 단 하나의 블록도 실행되지 않으면 예외는 처리되지 않는다. 어떻게든 처리를 하고 싶다면 모든 예외 클래스의 슈퍼 클래스인 Exception클래스 타입의 참조변수를 선언해 처리할 수 있다.
  • 블록 안에 새로운 try-catch 문이 들어갈 수 있다. 이때 catch 블록의 참조변수가 중복될 수 없다.
  • finally는 예외 발생 여부와 상관없이 실행되는 블록이다.

✔️ printStackTrace(), getMessage()

예외가 발생했을 때 만들어지는 예외 클래스의 인스턴스에는 예외에 대한 정보를 가지고 있다. 

다음 2가지 메서드를 통해서 정보를 얻을 수 있다.

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

✔️ 멀티 catch블록

JDK 1.7부터 지원하는 기능으로 여러 개의 catch 블록을  기호로 하나로 합칠 수 있다.

  • 나열된 예외 클래스들 중 부모-자식 관계가 있다면 오류가 발생한다.
  • 하나의 블록으로 여러 예외를 처리하는 것이므로 예외가 어디에 속한 것인지 알 수 없다.
// Example
public static void exceptionMultiCatch() {
      try {
         System.out.println(1/0);
      } catch (IllegalArgumentException | ArithmeticException exception) {
         System.out.println(exception.getMessage()); // / by zero
      }
}

✔️ throw

throw 키워드를 사용하면 고의적으로 예외를 발생시킬 수 있다.

// Example
public static void exceptionThrowError() {
   String name = new String("희조");
   try {
      if (name.equals("희조"))
         throw new IllegalArgumentException("감히 부를 수 없는 이름입니다.");
   } catch (Exception e) {
      e.printStackTrace();
   }
}

ㅋㅋ

✔️ throws

throws 키워드를 사용하면 메서드에 예외를 선언할 수 있다.

여기서 말하는 예외 선언은 예외 처리가 아닌 이 메서드를 호출했을 때 어떤 예외를 처리해야 하는지 명시해주는 것이다.

사용처가 너무 많은 메서드에 대해서 하나하나 예외 처리하지 않고 한 곳에서 예외를 처리하기 위함이다.

추가적인 참고사항은 다음과 같다.

  • RuntimeException 클래스는 적지 않는다. 반드시 처리되어야 하는 예외들만 선언한다.
  • 단순하게 예외가 전달하므로 프로그램이 비정상적으로 종료되지 않도록 어느 곳에서는 반드시 예외 처리해야 한다.
// Example
public static void exceptionThrowsMethod(int a, int b) throws ArithmeticException {
   System.out.println((float)a / b);
}

public static void main(String[] args) {
   try {
      exceptionThrowsMethod(1,0);
   } catch(Exception e) {
      System.out.println("main에서 예외가 처리되었다..!!");
      System.out.println(e);
   }
}

✔️ 자동 자원 반환, try-with-resource

기존에 입출력에 사용되는 클래스 중 사용했던 자원을 반환하기 위해서 꼭 닫아 줘야 하는 것들이 있다.

// Example, before JDK 1.7
try {
   fis = new FileInputStream("score.dat");
   dis = new DataInputStream(fis);
   ...
} catch (IOException ie) {
   ie.printStackTrace();
} finally {
   dis.close();
}

JDK 1.7부터 try-with-resource이 추가되어 try 블록을 벗어나는 순간 자동적으로 close()가 호출된다.

// Example, After JDK 1.7
try ( fis = new FileInputStream("score.dat");
   dis = new DataInputStream(fis)) {
   ...
} catch (IOException ie) {
   ie.printStackTrace();
}

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

앞서 말했듯이 자바는 오류를 Erro와 Exception으로 구분해서 관리한다.

둘 모두 클래스이므로 Object 클래스의 자손이며 모든 예외의 부모 Exception 클래스이다.

Java의 예외 계층


📌 Exception VS Error

Oracle 공식 문서에서 정의된 내용에 따르면 Java의 예외는 3개의 종류가 있다.

  • Error : 프로그램 외부에 있는 예상하고 복구할 수 없는 예외
  • Checked Exception : 프로그램에서 예상하고 복구해야 하는 예외
  • Runtime Exception : 프로그램 내부에 있는 예상하고 복구할 수 없는 예외, 일반적으로 논리 오류와 API의 부적절한 사용과 같은 프로그래밍 버그

✔️ Error

Error란 결국, 개발자가 제어할 수 없는 심각한 수준의 오류를 말한다. (ex. OutOfMemoryError 등)

공식 문서에서 Error는 개발자가 catch할 수도, 하지도 않아야 한다고 가이드한다.

따라서, 우리가 잘 처리해야 하는 것은 Exception이다.


📌 RuntimeException, Non-RuntimeException

예외 계층을 살펴보면 RuntimeException 클래스의 상속 여부로 예외가 둘로 나뉜다.

이 RuntimeException 클래스의 상속은 "반드시 처리해야 할까?"의 기준이 된다.

 

공식문서에 따르면 Unchecked Exception은 일반적으로 예상할 수 없는 상황에서 발생하는 예외가 아니고

보통 개발자의 잘못된 메서드 호출 등으로 발생하는 경우가 대부분이다.

따라서, 굳이 Unchecked Exception를 처리하지 않는 것이 프로그램의 명확성을 살리는 방법이다.


📌 커스텀 예외 만들기

기존에 정의된 예외 외에 개발자가 새로운 예외를 정의할 수 있다.

우리는 예외의 종류가 있다는 걸 알고 있기 때문에 상속받는 클래스에 따라 다른 예외를 구현할 수 있다.

//Example
public class customedCheckedException extends Exception {
	customedCheckedException(String msg) {
    	super(msg);
    }
}

public class customedUncheckedException extends RuntimeException {
	customedUncheckedException(String msg) {
    	super(msg);
    }
}

커스텀 예외를 만들 때 다음 사항들을 따르는 것이 좋다.

  • 정말 필요할 때 커스텀 예외를 구현하기
  • 인터페이스 구현체처럼 끝에 Exception으로 네이밍 컨벤션을 맞춰주기
  • 예외의 Cause를 설정할 수 있는 생성자 제공하기
  • 예외를 문서화해서 다른 개발자도 이해시키기

 

 

Reference : https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html

'언어 공부 > Java' 카테고리의 다른 글

[Java] Reflection  (0) 2023.01.11
[Java] 멀티쓰레드 프로그래밍 (22.12.05 updated)  (0) 2022.12.05
[Java] Java 인터페이스  (0) 2022.09.08
[Java] Java의 패키지  (0) 2022.09.05
[Java] Java의 상속  (2) 2022.08.25

댓글