Preface
이번 장에선 예외의 종류와 처리 방법에 대해 공부했다.
가끔 자바 문장을 볼 때마다 try, catch, finally, throws 등 익숙치 않은 키워드가 자주 보여서 코드를 완벽히 이해할 수 없었는데, 이제야 조금 알 것 같다.
예외 처리라고 해서 꽤나 복잡하고 어려울 것이라고 생각했는데, 걱정했던 것과 달리 처리 방법 자체는 단순한 것 같다.
다만, 발생할 수 있는 실행 예외를 예측하여 코드를 작성하는 것은 상당한 내공이 필요해 보인다.
1. 예외의 종류와 처리 방법
- 에러(error): 하드웨어의 오작동 또는 고장으로 인해 응용 프로그램 실행 오류가 발생하는 것
- 예외(exception): 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인한 프로그램 오류
- 예외의 종류
1) 일반 예외(Exception: 컴파일러 체크 예외): 소스를 컴파일하는 과정에서 검사하는 예외
2) 실행 예외(Runtime Exception): 컴파일하는 과정에서 예외 처리 코드를 검사하지 않는 예외
- 모든 예외 클래스들은 java.lang.Exception 클래스를 상속받지만, 실행 예외는 이와 더불어 RuntimeExeption도 상속받는다.
- 자바 프로그램에서 자주 발생하는 예외
1) NullPointerException: 객체 참조가 없는 상태, 즉 null 값을 갖는 참조 변수로 객체 접근 연산자인 도트를 사용했을 때 발생하는 일반 예외
2) ArrayIndexOutOfBoundsException: 배열에서 인덱스 범위를 초과하여 사용할 경우 발생하는 실행 예외
3) NumberFormatException: 문자열을 숫자로 변환하는 메소드에서 숫자로 변환될 수 없는 문자가 포함되어있을 때 발생하는 일반 예외
4) ClassCastException: 억지로 타입 변환을 시도할 경우 발생하는 일반 예외
→ 캐스팅은 상위 클래스와 하위 클래스, 구현 클래스와 인터페이스 사이에서만 가능하며, 이를 확인하기 위해 instanceof 연산자를 사용하는 것이 좋다.
- 예외 처리 코드: 프로그램에서 예외가 발생했을 경우 정상 실행을 유지할 수 있도록 처리하는 코드
- try-catch-finally 블록: 생성자 내부와 메소드 내부에서 작성되어 일반 예외와 실행 예외가 발생할 경우 예외 처리를 할 수 있도록 해준다.
1) finally 예외 발생 여부와 상관 없이 항상 실행되지만, 선택적이므로 생략할 수 있다.
2) try 블록과 catch 블록에서 return문을 사용하더라도 finally 블록은 항상 실행된다.
- Class.fonName( ) 메소드는 매개값으로 주어진 클래스가 존재하면 Class 객체를 리턴하지만, 존재하지 않으면 ClassNotFoundException 예외를 발생시킨다.
- catch 블록이 여러 개라 할지라도 단 하나의 catch 블록만 실행된다.
- 다중 catch 블록을 작성할 땐 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 한다.
→ 상위 예외 클래스의 catch 블록이 위에 있다면, 하위 예외 클래스의 catch 블록은 실행되지 않는다.
- 멀티(multi) catch 기능: 하나의 catch 블록에서 여러 개의 예외를 처리할 수 있는 기능
→ catch 괄호 안에 동일하게 처리하고 싶은 예외를 '|'로 연결하면 된다.
catch(ArrayIndexOutOfBoundsException | NumberFormatException e) {
예외 처리
}
- try-with-resources: 예외 발생 여부와 상관없이 사용했던 리소스 객체(입출력 스트림, 서버 소켓, 소켓, 각종 채널 등)의 close( ) 메소드를 호출해서 안전하게 리소스를 닫아준다. (try 키워드 옆에 소괄호로 리소스를 표현하는 듯?)
→ 리소스 객체는 java.lang.AutoCloseable 인터페이스를 구현하고 있어야 한다.
package ch10;
class FileInputStream implements AutoCloseable {
private String file;
public FileInputStream(String file) {
this.file = file;
}
public void read() {
System.out.println(file + "을 읽습니다.");
}
@Override
public void close() throws Exception {
System.out.println(file + "을 닫습니다.");
}
}
public class TryWithResourcesExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("file.txt")) {
fis.read();
throw new Exception();
} catch (Exception e) {
System.out.println("예외처리 코드가 실행되었습니다.");
}
}
}
- throws 키워드: 메소드 선언부 끝에 작성되어 메소드에서 처리하지 않은 예외를 호출한 곳으로 떠넘기는 역할을 한다.
1) throws 키워드 뒤에는 떠넘길 예외 클래스를 쉼표로 구분해서 나열한다.
2) 발생할 수 있는 예외의 모든 종류를 나열하는 것이 일반적이지만, thorws Exception만으로 모든 예외를 떠넘길 수도 있다.
3) throws 키워드가 붙어있는 메소드는 반드시 try 블록 내에서 호출되어야 하며, catch 블록에서 떠넘겨 받은 예외를 처리해야 한다.
4) 예외를 넘겨 받은 곳에서 다시 예외를 떠넘길 수도 있다.
5) main 메소드에서 throws 키워드를 사용하여 JVM에게 예외를 떠넘기면 예외의 내용이 콘솔 창에 출력된다.
→ 바람직하지 않은 처리 방법이다.
- 애플리케이션 예외(사용자 정의 예외): 애플리케이션 서비스와 관련된 예외
1) 일반 예외로 선언할 경우 Exception을 상속한다.
2) 실행 예외로 선언할 경우 RuntimeException을 상속한다.
3) 예외 클래스 이름은 Exception으로 끝나는 것이 좋다.
4) 대부분 생성자 선언만을 포함한다.
→ 보통 매개 변수가 없는 기본 생성자와, 예외 발생 원인(예외 메시지)을 전달하기 위해 String 타입의 매개 변수를 갖는 생성자 두 개를 생성하며, 후자는 상위 클래스의 생성자를 호출(super)하여 예외 메시지를 넘겨준다.
public class XXXException extends [Exception | RuntimeException] {
public XXXException() { }
public XXXException(String message) { super(message); }
}
- 예외를 발생시키는 방법
→ 예외를 발생시킬 때 String 타입의 메시지를 갖는 생성자를 이용하면 메시지는 자동적으로 예외 객체 내부에 저장된다.
throws new XXXException();
throws new XXXException("메시지");
- 모든 예외 객체는 Exception 클래스를 상속하므로 Exception이 가지고 있는 메소드를 모든 예외 객체에서 호출할 수 있다.
1) getMessage( ): catch 블록에서 예외 메시지를 얻는 메소드
2) printStackTrace( ): 예외 발생 코드를 추적해서 모두 콘솔에 출력하는 메소드
try {
예외 발생 문장
} catch(예외클래스 e) {
String message = e.getMessage();
e.printStackTrace();
}
'Java > 이것이 자바다' 카테고리의 다른 글
기본 API 클래스 (2) (0) | 2023.04.14 |
---|---|
기본 API 클래스 (1) (0) | 2023.04.13 |
이자바 9장(중첩 클래스와 중첩 인터페이스) 확인문제 (0) | 2023.03.17 |
중첩 클래스와 중첩 인터페이스 (0) | 2023.03.15 |
이자바 8장(인터페이스) 확인문제 (0) | 2023.03.14 |
댓글