✍️ 학습할 것
- 자바에서 예외 처리 (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 클래스이다.
📌 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 |
댓글