본문 바로가기
Java관련/Java

Java 예외 처리

by devstep 2022. 9. 29.

자바 예외처리

학습 주제

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

Exception과 Error의 차이

  1. 에러 error
  2. 예외 exception

Error(오류)는 시스템에 비정상적인 상황이 생겼을 때 발생합니다. 시스템 레벨에서 발생하기 때문에 심각한 수준의 오류이고 프로그램의 비정상적인 종료를 개발자가 미리 예측하여 처리할 수 없습니다.
예를 들면 메모리 부족(OutOfMemoryError), StackOverFlowError 등이 있습니다.

Exception(예외)는 개발자가 구현한 로직에서 발생하므로 미리 예측하여 처리할 수 있습니다. 프로그램의 비정상적인 종료를 막기위해 예외를 구분하고 그에 따른 처리 방법을 명확히 알고 적용하는 것이 중요합니다.

발생한 예외를 처리하지 못하면 비정상적으로 프로그램이 종료되고 처리되지 못한 예외는 JVM의 예외처리기(UncaughtException)가 받아서 예외의 원인을 화면에 출력합니다.

예외 처리(exception handling)는 프로그램 실행 시 발행할 수 있는 예외에 대비한 코드를 작성하는 것으로 프로그램의 비정상 종료를 막고 정상적인 실행상태를 유지하게 합니다.

자바에서 예외 처리 방법

1. try ~ catch문

  • 구조

여러 종류의 예외를 처리할 수 있도록 하나 이상의 catch 블럭이 올 수 있습니다. 여러 개의 catch 블럭 중 발생한 예외 종류와 일치하는 단 한 개의 catch 블럭만 수행됩니다. 그리고 일치하는 catch 블럭이 없으면 예외는 처리되지 않습니다.

try {
    // 예외 발생 가능성 문장
} catch (Exception1 el) {
    //Exception1이 발생할 경우 처리하기 위한 문장
} catch (Exception2 e2) {
    //Exception2이 발생할 경우 처리하기 위한 문장
}
  • exception 참조변수 e

catch 블럭의 괄호 내에 선언된 변수는 catch 블럭 내에서만 유효하기 때문에 모든 catch 블럭에 참조변수를 e 하나만 사용해도 됩니다.

예외 발생과 catch 블럭

catch 블럭은 괄호()와 블럭{}으로 나눠져 있습니다.
괄호() 내에는 처리하고자 하는 예외와 같은 타입의 참조변수 하나를 선언해야 합니다.

예외가 발생하면 아래와 같은 순서로 진행됩니다.

  1. 발생한 예외 클래스의 인스턴스가 만들어진다.
  2. 예외 발생 문장이 try블럭에 포함되어 있으면 예외 처리할 catch 블럭이 있는지 찾는다.
  3. instanceof연산자로 catch 블럭의 괄호()내에 선언된 참조변수와 생성된 예외 클래스의 인스턴스를 비교한다.
  4. 검색결과가 true인 catch 블럭을 찾으면 블럭 내 문장들을 수행 후 try-catch문을 빠져나간다.

printStackTrace(), getMessage()

발생 예외 정보가 담겨 있고 catch블럭 괄호에 선언된 참조 변수를 통해 예외 인스턴스에 접근할 수 있습니다.

  • printStackTrace() : 예외 발생 당시 호출 스택에 있었던 메소드의 정보와 예외 메시지를 화면에 출력
    printStackTrace(PrintStream s), printStackTrace(PrintWriter s) 를 이용해 예외 정보를 파일에 저장
  • getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지

멀티 catch 블럭

| 를 이용해서 여러 catch블럭을 하나의 블럭으로 합칠 수 있는 것으로 JDK1.7부터 사용 가능합니다.

| 로 연결된 예외 클래스가 조상/자손 관계에 있으면 컴파일 에러가 발생합니다. 조상클래스만 써주는 것과 동일하기 때문에 불필요한 코드 제거의 의미로 컴파일 에러가 발생합니다.

발생 예외를 멀티 catch 블럭으로 처리하면 어떤 예외가 발생한 것인지 알수 없습니다. 그래서 {} 블럭에서 예외처리할 때 | 로 연결된 예외 클래스의 조상 클래스의 멤버만 사용가능 합니다.

finally

finally 블록 내의 문장은 try-catch 블록에 return문이 있더라도 실행됩니다.

try-with-resource

JDK1.7부터 자원 해제를 해주는 try-with-resources 구문이 추가되었습니다.

기존에는 시스템 자원을 사용하는 코드의 경우 finally 구문을 통해서 반드시 자원을 닫아주는 형태로 코드를 작성했습니다.

try-with-resource는 try안 괄호() 구문에 자원 할당 코드를 작성하면 해당 자원은 블록{} 수행 이후 자동으로 반환 됩니다. 자동으로 반환되기 위해서는 AutoCloseable 인터페이스를 구현한 클래스여야 합니다.

public static void main(String args[]) {  
  try (  
    FileInputStream is = new FileInputStream("file.txt");  
    BufferedInputStream bis = new BufferedInputStream(is)  
  ) {  
      //... do something  
  } catch (IOException e) {  
      // 에러처리  
  }  
}  

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

자바는 실행시 발생할 수 있는 Exception과 Error로 분류되는 오류를 클래스로 정의하였습니다.

자바 예외 계층 구조(image: madplay.github.io/post/java-checked-unchecked-exceptions)

모든 예외의 최고 조상은 Exception클래스입니다.
예외 클래스는 두 그룹으로 나뉠 수 있습니다.

  1. checked Exception : RuntimeException과 자손들을 제외한 Exception 클래스와 그 자손들
  2. unchecked Exception : RuntimeException클래스와 그 자손들

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

RuntimeException은 unchecked exception이고, 그 외 익셉션은 checked exception입니다.

checked exception은 예외 발생한 곳에서 처리되거나 메서드의 선언부에 예외를 선언해주어 호출한 곳에서 처리되게 해야 합니다.

unchecked exception은 컴파일 단계에서 확인하지 않는 예외입니다. 이 예외들은 컴파일러가 예외를 처리하거나 선언하도록 강제하지 않으므로 프로그래머가 알아서 처리 해야 합니다.

checked Exception인 Exception클래스들은 사용자의 실수와 같은 외적인 요인에 의해 발생하는 경우가 많습니다.

  • FileNotFoundException : 존재하지 않은 파일의 이름을 입력했을 때 예외
  • ClassNotFoundException
  • DataFormatException : 입력한 데이터 형식 잘못된 예외

unchecked Exception인 RuntimeException 클래스들은 주로 개발자 실수에 의해 발생할 수 있는 예외로 자바의 프로그래밍 요소와 관계가 있습니다. 몇가지 예를 들면 아래와 같습니다.

  • ArrayIndexOutOfBoundException : 배열의 범위를 벗어나는 예외
  • NullPointException : 값이 null인 참조변수의 멤버 호출 예외
  • ClassCastException : 클래스간 형변환을 잘못한 예외
  • ArithmeticException : 정수를 0으로 나눌 때 발생하는 예외

Runtime Exception은 왜 예외를 명세하지 않아도 되도록 했을까? 에 대한 물음에 오라클 공식문서를 요약한 블로그 내용을 인용합니다.
https://wisdom-and-record.tistory.com/46

예외는 메서드의 파라미터나 반환 값만큼이나 중요한 공용 인터페이스 중 하나이다. 
메서드를 호출하는 쪽은 그 메서드가 어떤 예외를 발생시킬 수 있는가에 대해 반드시 알아야 한다. 
따라서 Java는 checked exception을 통해 해당 메서드가 발생시킬 수 있는 예외를 명세하도록 강제하고 있다.

그럼 Runtime Exception은 왜 예외를 명세하지 않아도 되도록 했을까? 
Runtime Exception은 프로그램 코드의 문제로 발생하는 예외이다(즉 코드를 짠 개발자의 잘못). 
따라서 클라이언트 쪽(메서드를 호출하는 쪽)에서 이를 복구(or 회복)하거나 대처할 수 있을 거라고 예상하긴 어렵다. 
또 Runtime Exception은 프로그램 어디서나 매우 빈번하게 발생할 수 있기 때문에 
모든 Runtime Exception을 메서드에 명시하도록 강제하는 것은 프로그램의 명확성을 떨어뜨릴 수 있다.

따라서 클라이언트가 exception을 적절히 회복할 수 있을 것이라고 예상되는 경우 checked exception으로 만들고, 
그렇지 않은 경우 unchecked exception으로 만드는 것이 좋다.

커스텀한 예외 만드는 방법


참고자료

'Java관련 > Java' 카테고리의 다른 글

Java enum  (0) 2022.11.04
Java 클래스  (0) 2022.09.30
Java static, static final, final 차이  (0) 2022.09.15
자바 상속의 메모리 할당 과정  (0) 2022.09.05
Java 상속, 오버라이딩, 다이나믹 메소드 디스패치  (0) 2022.08.29

댓글