[Java] 예외

sobam·2022년 10월 7일
0

자바

목록 보기
9/18
post-thumbnail

📔 학습한 내용을 정리하기 위해 작성하는 게시글입니다.

오류(error)와 예외(exception)란?


  • 오류(error) : 자바 가상 머신 자체에서 발생하는 오류로, 개발자가 해결할 수 없는 오류
    ex) 메모리가 꽉 찬 경우, 스레드가 죽은 경우, 블루스크린

  • 예외(exception) : 연산 오류, 숫자 포맷 오류 등과 같이 상황에 따라 개발자가 해결할 수 있는(오류 자체를 수정할 수 있다는 뜻이 아님, 오류가 발생했을 때 차선책을 선택하는 것) 오류

예외 처리(Exception Handling) : 오류가 발생했을 때 차선책을 제시함으로써 오류를 피하는 과정
→ 개발자가 예외 처리하면 프로그램은 종료되지 않고 계속 실행된다.



예외의 상속 구조와 종류


  • 자바의 예외 클래스는 java.lang.Exception을 상속 받는다.
  • 오류와 예외의 최상위 클래스인 Error, Exception 모두 Throwable 클래스를 상속하고 있다.
  • 예외는 일반 예외와 실행 예외로 구분된다.

일반 예외(=검사형 예외, Checked Exception)

  • 예외 처리를 하지 않으면 컴파일 오류가 발생하므로 꼭 처리해야 하는 검사형 예외
  • 컴파일 단계에서 체크
  • 예외 처리 필수
  • roll-back 안함

실행 예외(Unchecked Exception=Runtime Exception)

  • 예외 처리를 하지 않아도 컴파일할 수 있는 비검사형 예외
  • RuntimeException 클래스를 상속받은 클래스
  • 실행 단계에서 체크
  • 예외 처리 생략 가능(필요에 따라 처리)
  • roll-back

대표적인 일반 예외

일반 예외 종류발생 이유
ClassNotFoundException존재하지 않는 클래스를 사용하려고 할 때 발생
NoSuchFieldException클래스가 명시한 필드를 포함하지 않을 때 발생
IOException데이터 읽기 쓰기 같은 입출력 문제가 있을 때 발생

대표적인 실행 예외

실행 예외 종류발생 이유
ArtithmeticException0으로 나누기와 같은 부적절한 산술 연산을 수행할 때 발생
IllegalArgumentException메서드에 부적절한 매개변수를 전달할 때 발생
IndexOutOfBoundException배열, 벡터 등에서 범위를 벗어난 인덱스를 사용할 때 발생
NoSuchElementException요구한 원소가 없을 때 발생
NullPointerExceptionnull값을 가진 참조 변수에 접근할 때 발생
NumberFormatException숫자로 바꿀 수 없는 문자열을 숫자로 변환하려 할 때 발생


예외 처리 방법


예외 처리 방법에는
1) 예외 복구 2) 예외 회피 3) 예외 전환 4) 사용자 정의 예외가 있다.

1) 예외 복구 : try~catch~(finally)

예외 상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 방법
(기본 작업 흐름이 불가능하면 다른 작업 흐름으로 자연스럽게 유도)

예외 처리 문법은 크게 3가지 요소(try, catch, finally)로 구성되어 있다.

예외 처리 구문

try {
	//일반 예외, 실행 예외 발생 가능성이 있는 코드
} catch (예외 클래스명 참조 변수명) {
	//try 블록에서 예외가 발생했을 때 처리
} finally { //생략 가능
	//예외 발생 여부에 상관없이 무조건 실행
}

다중 예외 처리 구문

try {
	//...
} catch (예외 타입 e1) {
	//...
} catch (예외 타입 e2) {
	//...
}
...
finally {
	//...
}

다중 예외 처리 구문2(java7 이상)

try {
	//...
} catch (예외 타입 e1 | 예외 타입 e2) {
	//...
}
...
finally {
	//...
}
  • 다중 예외 처리 구문을 작성할 때 주의해야 할 사항은 try{} 블록에서 예외가 발생하고, 여러 개의 catch(){} 블록이 있을 때 실행할 catch(){} 블록의 선택 과정은 항상 위에서부터 확인한다.
  • OR(|) 연산자를 이용하면 하나의 catch() 블록에서 2개 이상의 예외 처리 가능

리소스 자동 해제 예외 처리 구문

try (리소스 자동 해제가 필요한 객체 생성) {
	//예외 발생 가능 코드
}
catch (예외 클래스명 참조 변수명) {
	//해당 예외가 발생했을 때 처리하는 블록
}
finally {
	//예외 발생 여부에 상관없이 무조건 실행하는 블록
}
  • 리소스 해제는 더이상 사용하지 않는 자원을 반납하는 것

  • 예외의 발생 여부와 관계 없이 예외 처리 구문이 완료된 후 리소스 객체의 close() 메서드를 자동으로 호출해 줌
    → 리소스 객체 내부에 close() 메서드가 있어야 자동 해제를 할 수 있는 객체가 됨

  • 리소스 자동 해제 객체는 반드시 close() 메서드를 포함해야 함 = 리소스 자동 해제 객체는 AutoCloseable 인터페이스를 구현해야 함

java.lang.AutoCloseable 인터페이스에는 close() 추상 메서드가 포함되어 있다.

  • 사용 예)
    열어 둔 파일이 있다면 해당 파일을 닫아야 다른 프로그램이 파일을 사용할 수 있음, 메모리에 엄청난 크기의 객체를 만들어서 사용했을 때엔 사용이 완료되면 메모리 공간 확보를 위해 리소스를 해제해야 함, 자바 입출력 리소스 등

2) 예외 회피 : throws

예외 처리를 직접 담당하지 않고 throws를 통해 호출한 쪽으로 예외를 던지고 그 처리를 회피하는 방법이다.
(현재 메서드 안에서 예외 처리를 하지 않고, 현재 메서드를 호출한 메서드로 예외 처리를 떠넘김)

무책임하게 던지는 것은 위험하며, 호출한 쪽에서 다시 예외를 받아 처리하도록 하거나 해당 메서드에서 이 예외를 던지는 것이 최선의 방법인 경우에 사용

1) throws문을 선언해서 던지거나, 2) catch문으로 일단 예외를 잡은 후 로그를 남기고 던짐

예외 회피 구문

리턴 타입 메서드명(입력매개변수) throws 예외 클래스명 {
	//예외 발생 코드
}

다중 예외 회피 구문

리턴 타입 메서드명(입력매개변수) throws 예외 클래스 A, 예외 클래스 B ... {
	//여러 개의 예외 종류가 발생할 수 있는 블록
}

→ 다음과 같이 예외클래스 종류별로 나열하는 것이 일반적이지만, Exception로 모든 예외를 간단히 떠넘길 수 있다.


3) 예외 전환

예외 회피와 비슷하게 메서드 밖으로 예외를 던지지만, 발생한 예외를 그대로 던지지 않고 적절한 예외로 전환해서 넘기는 방법이다.

이때, 기존 예외를 담아서 중첩 예외로 만드는 것이 좋다.

  • 호출한 쪽에서 예외를 받아서 처리할 때 더 명확하게 인지할 수 있음

  • 주로 강제하는 체크 예외를 언체크 예외인 런타임 예외로 바꾸는 경우에 사용

  • 예외 전환은 2가지 목적으로 사용됨
    1) 예외 상황에 대한 적절한 의미를 부여해주지 못하는 경우
    2) 예외를 처리하기 쉽고 단순하게 만들기 위해 포장

예외 전환 구문 예

catch(exception e) {
	...
    throw 예외 클래스명(e);
}

4) 사용자 정의 예외

프로그램을 개발하다보면 자바 표준 API가 제공하는 예외 처리 클래스 이외에 개발하는 프로그램에 따라 다양한 예외 상황이 발생할 수 있다.

이때, 예외 클래스를 직접 정의하여 사용하는 예외를 사용자 정의 예외라고 한다.

  • 기존 JDK에서 제공하는 예외 클래스 중 가장 유사한 클래스를 상속(기본적으로는 Exception 클래스를 상속)
  • 예외 생성 : throw new 사용자정의예외()
  • Exception을 상속한 일반 예외와 RuntimeException을 상속한 실행 예외로 선언하는 방법으로 나뉨(자바에서 제공하는 예외 클래스와 동일)

5) 사용자 정의 예외 클래스 생성 방법

단계 1) 예외 클래스를 사용자가 직접 정의

일반 예외 구문

class MyException extends Exception {
	MyException() {
    }
    MyException(String s) { //s는 예외 메시지
    	super(s); //부모 생성자 호출
    }
}

실행 예외 구문

class MyRTException extends RuntimeException {
	MyRTException() {
    }
    MyRTException(String s) { //s는 예외 메시지
    	super(s); //부모 생성자 호출
    }
}

일반 예외 클래스는 Exception을, 실행 예외 클래스는 RuntimeException을 상속해 정의한다.

단계 2) 작성한 예외 클래스를 이용해 객체 생성

일반 예외 구문

MyException me1 = new MyException();
MyException me2 = new MyException("예외 메시지");

실행 예외 구문

MyRTException me1 = new MyRTException();
MyRTException me2 = new MyRTException("예외 메시지");

객체를 생성하는 방법은 일반 예외와 실행 예외가 동일

단계 3) 고려하는 예외 상황에서 예외 객체를 던짐(throw)

일반 예외 구문

throw me1; //예외 발생시키기
throw me2;
throw new MyException();
throw new MyException("예외 메시지");

실행 예외 구문

throw me1; //예외 발생시키기
throw me2;
throw new MyException();
throw new MyException("예외 메시지");
  • 예외 객체를 던진다 = 실제 자바 가상 머신에게 예외 객체를 만들어 전달한다
  • throw로 예외를 던지면 곧바로 예외가 발생하므로 적절한 예외 처리 구문을 반드시 작성해 놓아야 함(해당 메서드 안에서 직접 처리, 예외를 상위 메서드로 전가해 예외 처리)
  • throw 키워드는 예외 객체를 던지는 기능을 수행하는 것으로, 예외 객체를 전가하는 throws와 혼동하지 않도록 주의

6) 예외 클래스의 메서드

getMessage() 메서드

  • getMessage()는 예외가 발생했을 때 생성자로 넘긴 메시지를 문자열 형태로 리턴하는 메서드이다.
public String getMessage()

printStackTrace() 메서드

  • printStackTrace()는 예외 발생이 전달되는 경로, 즉 예외가 전가된 과정을 한눈에 확인할 수 있는 메서드이다.
public void printStackTrace()

📌 try~catch문으로 예외를 잡아놓고 catch를 비워두면 예외가 발생했을 때 원인을 파악하기 어려워 유지보수에 어려움을 겪을 수 있으므로 예외 처리 시 신중해야 한다.

📌 예외 처리를 할 때에는 로그(log)를 남기는 것이 매우 중요하다. 체계적인 로그를 남겨야 오류가 발생했을 때 로그를 보고 오류가 발생하는 코드를 순서대로 따라가며 확인할 수 있고, 원인을 찾을 수 있다.



🔔 referance

<이재환의 자바 프로그래밍 입문>
<Do it! 자바프로그래밍 입문>
<Do it! 자바 완전 정복>
https://www.nextree.co.kr/p3239/
https://butter-shower.tistory.com/87
https://qh5944.tistory.com/32
https://madplay.github.io/post/java-checked-unchecked-exceptions
https://europani.github.io/java/2020/05/22/006-exception.html

0개의 댓글

관련 채용 정보