본문 바로가기
개발언어/Java : 자바

자바 쉽게 배우기 11 - 예외 처리

by 개발자D 2023. 1. 23.

예외 처리

예외

한 번에 완벽한 코드를 짤 수 있다면 얼마나 좋을까요? 하지만 그것은 환상에 가깝습니다. 항상 수많은 에러들이 프로그래머들을 기다리고 있습니다. 에러를 수정하는 일은 프로그래머들의 숙명입니다. 조각가들처럼 큰 형태를 만들고 세밀하게 깎아나가듯 에러를 잡아가며 프로그램을 완성합니다. [예외]는 프로그램 에러 중 런타임 에러의 한 종류입니다. 그럼 프로그램 에러부터 자세히 들어가 봅시다.

 

프로그램 에러

프로그램을 오작동시키는 에러에는 3가지 종류가 있습니다. 

컴파일 에러 컴파일 시에 발생하는 에러로 컴파일러에 의해 어느 부분이 문제가 있는 지 알 수 있다.
예시) 오타, 구문오류, 자료형오류
런타임 에러 실행 시에 발생하는 에러
예시 ) 에러, 예외 
논리적 에러 실행은 되지만, 의도와는 다르게 동작하는 것

 

런타임 에러

에러(error) 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
예외(exception) 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류

자바에는 에러(error)와 예외(exception) 클래스가 정의되어 있습니다. 

 

예외 (Exception 클래스) : java.lang.Exception

RuntimeException (unchecked예외)

실행 예외
  • RuntimeException 클래스와 그 자손들
  • 프로그래머의 실수로 발생
  • 처리하지 않으면 컴파일 에러는 발생하지 않지만 런타임 에러가 발생
    (예외처리가 강제되지 않음)
Exception (checked예외) 

일반 예외
  • Exception 클래스에서 RuntimeException 클래스를 제외한 클래스들과 그 자손들
  • 사용자의 실수와 같은 외적인 요인에 의해 발생
  • 처리하지 않으면 컴파일 에러 발생
    (예외처리가 강제됨)

 

 

 


예외 처리(exception handling)

예외 처리는 Exception 클래스에 속하는 예외들을 처리하는 작업입니다. 프로그래머들은 프로그램 실행 시 발생할 수 있는 예외들을 예측하여 이를 대비하는 코드를 작성해야 합니다. 예외 처리를 통해 프로그램의 비정상적인 종료를 막고, 정상적인 실행상태를 유지할 수 있습니다. 

 

예외를 처리하는 방법 - try-catch / 메서드에 예외 선언 / try-with-resources / 사용자 예외

예외를 처리하는 4가지 방법이 있습니다. 

방법 특징
try-catch 직접 예외 처리
메서드에 예외 선언하기 (throws) 메서드를 호출한 곳으로 예외 처리 떠넘기기
try-with-resources 자동 자원 반환
사용자 예외 새로운 예외 클래스 정의

1. try-catch

try {
	//예외가 발생할 가능성이 있는 코드
} catch (예외클래스 참조변수) {
	//예외가 발생할 경우, 이를 처리하기 위한 문장들
} ...
	//catch문은 여러개 사용 가능

예외클래스 위치에 예측가능한 예외의 클래스 이름을 적어줍니다. catch문은 여러 개 사용할 수 있습니다. try {} 블록과 catch {} 블록 안에는 또 다른 try-catch문이 들어갈 수 있습니다. 다만 에러의 참조변수 이름을 다르게 설정해주어야 합니다. 중첩반복문에서 외부와 내부에서 다른 변수를 사용하는 것과 같은 이치입니다.

 

try문에서 예외가 발생하면 발생한 예외에 해당하는 클래스의 인스턴스가 만들어집니다. 이후 예외와 일치하는 catch 블록이 있는지 찾기 위해 instanceof 연산자로 catch문의 괄호 안의 참조변수와 만들어진 예외 인스턴스를 비교합니다. 일치하는 변수가 있을 경우  catch문을 실행하고 try-catch문을 빠져나갑니다. 예외가 발생하지 않았거나 일치하는 catch 블록이 없을 경우 catch 블록은 실행되지 않고 try-catch문이 종료됩니다.

 

catch문은 순서대로 찾아져 실행되기 때문에 첫 번째 catch문에서 걸러진 예외는 두 번째 catch문의 예외에 해당하더라도 두 번째 catch문이 실행되지 않습니다. 따라서 더 큰 범위의 예외는 아래쪽에 작성하는 것이 바람직합니다.

try에 반환값이 있는 return문이 있을 경우 catch에서도 return문을 작성해 주어야 합니다. 

 

finally 블록

try {
	...
} catch (예외클래스 참조변수) {
	...
} finally {
	...
}

예외의 발생여부와 상관없이 무조건 실행되어야 하는 코드가 있을 때 사용합니다. try나 catch문에 return문이 있어도 finally문이 우선적으로 실행된 후 return 됩니다. 만약 finally문에 return문이 있을 경우 finally의 return 값이 반환됩니다.

[try] -> [catch] (예외가 없을 경우 생략) -> [finally] -> [try의 return || catch의 return] -> [finally의 return]

 

멀티 캐치블록

try {
} catch (Excaption1 | Excaption2 e) {}

‘|’ 기호로 예외 클래스를 연결할 수 있습니다. 연결 개수에는 제한이 없습니다. 다만 상위클래스와 하위클래스를 연결하는 것은 불필요하기 때문에 에러가 발생합니다.

 

예외 발생시키기

Exception e = new Exception();
throw e;
↓
throw new Exception(); // 한 줄로 쓸 수 있음
throw new Exception("에러발생");

Exception인스턴스를 생성할 때 String 형의 예외 인스턴스의 메시지를 저장할 수 있습니다.

 

printStackTrace() : 예외 발생 당시의 호출스택에 있었던 메서드의 정보와 예외 메시지를 화면에 출력합니다.

getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있습니다.

 

2. 메서드에 예외 선언하기

void method() throws Exception1, Exception2, ...{
}

메서드 내에 발생할 수 있는 예외(Exception클래스 예외)를 메서드 선언부에 적어줍니다. JavaAPI문서에서 메서드에서 발생할 수 있는 예외를 찾을 수 있습니다. 아래는 자바 8 버전의 API 문서입니다. 주소의 숫자를 바꿔 버전을 변경할 수 있습니다.

 

Java Platform SE 8

 

docs.oracle.com

메서드의 throws는 try-catch처럼 예외를 직접 처리하는 것이 아니라 호출한 메서드로 예외 처리를 넘기는 방식입니다. throws가 붙은 메서드를 사용할 때는 반드시 try 블록 안에서 호출하거나 예외처리를 떠넘겨야 합니다.

다른 메서드를 호출하는 가장 첫 번째 지점인 main 메서드에서도 예외가 처리되지 않으면 프로그램이 비정상적으로 종료됩니다.

예외 발생으로 메서드를 호출한 곳에서 다시 값을 받아와야 할 때 이 방법을 사용합니다.

 

3.  자동 자원 반환 - try-with-resources문

JDK1.7부터 try-with-resources문이 추가되었습니다. 입출력을 이용하기 위해서는 자원을 사용하고 반환해 주는 작업이 필요합니다. 자원을 반환하는 것은 필수이기 때문에 finally문 안에 작성해야 합니다. 중요한 것은 자원을 반환하는 데 사용하는 메서드 close()가 예외를 발생시킬 수 있다는 점입니다. 그렇기에 finally문 안에 try-catch문을 사용해 close()를 호출해야 합니다.

하지만 이 경우 코드가 복잡해지고 외부의 try 블록과 finally 블록에서 모두 예외가 발생하면 try 블록의 예외가 무시되는 문제가 있습니다. try-with-resources문을 사용하면 이러한 문제를 해결할 수 있습니다.

try (입출력 객체 생성) { // ';'로 구분해 여러 문장 입력 가능 
	...
} catch (예외클래스 참조변수) {
	...
} finally {
	...
}

try-with-resources문의 괄호() 안에 객체를 생성하는 문장을 넣으면, 이 객체는 따로 close()를 호출하지 않아도 try블록을 벗어나는 순간 자동으로 close()가 호출됩니다. try블록과 호출된 close() 메서드에서 모두 예외가 발생할 경우 close() 메서드의 예외는 억제된 예외로 저장됩니다.

 

❓ 억제된 예외란 무엇인가요?

발생하긴 했지만 부차적인 예외일 때 억제된 예외로 처리됩니다.

 

4. 사용자정의 예외 만들기

이미 정의되어 있는 예외가 아닌 새로운 예외를 정의할 수 있습니다. Exception클래스 또는 RuntimeException클래스로부터 상속받아 사용자정의 예외클래스를 만들어 줍니다.

class MyException extends Exception {
	MyException (String message) {
    	super(message);
    }
    
    // 추가할 내용 입력
}

 


예외 되던지기

예외를 처리한 후에 인위적으로 다시 발생시키는 방법을 통해 예외가 발생한 메서드호출한 메서드 양쪽에서 처리하게 합니다.

  1. 호출될 메서드에서 try-catch문을 사용해 예외 처리
  2. 호출될 메서드의 catch문 끝에서 throw를 사용해 예외 발생시키기
  3. 호출 메서드의 catch문에서 예외 처리
호출 메서드() {
    try { 
    	호출될 메서드
    } catch (예외클래스 참조변수) {
    	...
    }
}

호출될 메서드() throws 예외 {
    try {
        ...
    } catch (예외클래스 참조변수) {
        ...
        throw e;
    }
}

 

❓ 왜 이런 귀찮은 방법을 사용하죠? 

- 한 메서드에서 처리해야 할 예외가 여럿일 때 이러한 방법을 사용합니다.

 

연결된 예외

원인 예외  →  발생 예외

한 예외가 다른 예외를 발생시킬 때 발생시킨 예외는 [원인예외], 발생된 예외는 [발생예외]라고 부릅니다.

이때 발생예외의 원인예외를 등록하고, 이 발생예외를 던져 원인예외와 함께 해결하는 방법이 있습니다.

 

Throwable initCause (Throwable cause) : 지정한 예외를 원인 예외로 등록

RuntimeException (Throwable cause) : 원인예외를 등록하는 생성자 (checked예외를 uncheckeed 예외로 감싸는 데 사용)

Throwable getCause : 원인 예외를 반환

 

❓ 왜 이런 귀찮은 방법을 사용하죠? 

- 여러 예외를 하나의 큰 예외로 묶어서 다루기 위해 사용합니다.

- checked예외를 unchecked 예외로 바꿀 수 있습니다.

 


에러와 예외의 차이, 예외를 처리하는 방법에 대해 공부했습니다. 다음 글에서는 자바에서 날짜와 시간을 어떻게 다루는지에 대해 설명드리겠습니다. 

 

자바(JAVA) 쉽게 배우기 12 - 날짜와 시간(1)

날짜와 시간 자바의 날짜와 시간은 Date와 Calendar 클래스, time패키지를 사용해 나타냅니다. Date, Calendar, time 패키지 순으로 발전되어 왔으며 최근에는 Date, Calendar를 대신하여 time 패키지를 사용하

devdharu.tistory.com