[Java] 예외 처리 ③

kiteB·2022년 1월 29일
0

Java

목록 보기
27/35
post-thumbnail

[ 자동 리소스 닫기 ]

자바 7에서 새로 추가된 try-with-resources를 사용하면 예외 발생 여부와 상관없이 사용했던 리소스 객체(각종 입출력 스트림, 서버 소켓, 소켓, 각종 채널)의 close() 메소드를 호출해서 안전하게 리소스를 닫아준다.

여기에서 리소스는 데이터를 읽고 쓰는 객체를 말한다.
파일의 데이터를 읽는 FileInputStream 객체와 파일에 쓰는 FileOutputStream은 리소스 객체라고 할 수 있다.


1. 자바 6 이전까지의 방법

리소스 객체를 안전하게 닫기 위해서 자바 6 이전까지는 다음과 같이 복잡한 코드를 사용해야 했다.

FileInputStream fis = null;
try {
    fis = new FileInputStream(“file.txt”);} catch(IOException e) {} finally {
    if(fis != null) {
        try {
            fis.close();
        } catch(IOException e) { }
    }
}
  • finally 블록에서 다시 try-catch를 사용해서 close() 메소드를 예외 처리해야 하므로 다소 복잡하게 보인다.

2. try-with-resources를 사용한 방법

자바 7에서 추가된 try-wirh-resources문을 사용법은 다음과 같다.

try (파일을열거나자원을할당하는명령문){
    ...
}

try-with-resources문을 사용하면 다음과 같이 간단해진다.

try(FileInputStream fis = new FileInputStream(“file.txt”) {} catch(IOException e) {}
  • try 블록이 정상적으로 실행을 완료했거나 도중에 예외가 발생하게 되면 자동으로 FileInputStreamclose() 메소드가 호출된다.
  • try {}에서 예외가 발생하면 우선 close()로 리소스를 닫고 catch 블록을 실행한다.

만약 복수 개의 리소스를 사용해야 한다면 다음과 같이 작성하면 된다.

try(
    FileInputStream fis = new FileInputStream(“file1.txt”);
    FileOutputStream fos = new FileOutputStream(“file2.txt”)) {} catch(IOException e) {}

try-with-resources를 사용하기 위해서는 리소스 객체가 java.lang.AutoCloseable 인터페이스를 구현하고 있어야 한다. try-with-resourcesAutoCloseable에 정의되어있는 close()를 자동으로 호출한다.


[ 예외 떠넘기기 ]

메소드 내부에서 예외가 발생할 수 있는 코드를 작성할 때 try-catch 블록으로 예외를 처리하는 것이 기본이지만, 경우에 따라서는 throws 키워드를 이용하여 메소드를 호출한 곳으로 예외를 떠넘길 수도 있다. 이렇게 하면 해당 메소드를 사용할 때 발생할 수 있는 예외를 사용자가 충분히 인지할 수 있으며, 그에 대한 처리까지도 강제할 수 있다.

throws 키워드 사용 방법은 다음과 같다.

리턴타입 메소드명(매개변수,) throws 예외클래스1, 예외클래스2,{
}

throws 키워드 뒤에 떠넘길 예외 클래스를 쉼표로 구분해서 나열해주면 된다.

위와 같이 발생할 수 있는 예외의 종류별로 throws 뒤에 나열하는 것이 일반적이지만,
다음과 같이 throws Exception만으로 모든 예외를 간단히 떠넘길 수도 있다.

리턴타입 메소드명(매개변수,) throws Exception {
}

throws 키워드가 붙어있는 메소드는 반드시 try 블록 내에서 호출되어야 한다. 그리고 catch 블록에서 떠넘겨 받은 예외를 처리해야 한다.


예제

다음 코드는 throws 키워드가 있는 method2()method1()에서 호출하는 방법을 보여준다.

public void method1() {
    try {
        method2();
    } catch(ClassNotFoundException e) {
        //예외 처리 코드
        System.out.println(“클래스가 존재하지 않습니다.);
    }
}

public void method2() throws ClassNotFoundException {
    Class clazz = Class.forName(“java.lang.String2);
}

method1()에서도 try-catch 블록으로 예외를 처리하지 않고 throws 키워드로 다시 예외를 떠넘길 수 있다. 그러면 method1()을 호출하는 곳에서 결국 try-catch 블록을 사용해서 예외를 처리해야 한다.

public void method1() throws ClassNotFoundException {
    method2();
}

자바 API 도큐먼트

자바 API 도큐먼트를 보면 클래스 생성자와 메소드 선언부에 throws 키워드가 붙어있는 것을 흔히 볼 수 있다. 이러한 생성자와 메소드를 사용하고 싶다면, 반드시 try-catch 블록으로 예외 처리를 해야 한다. 아니면 throws를 다시 사용해서 예외를 호출한 곳으로 떠넘겨야 한다. 그렇지 않으면 컴파일 오류가 발생한다.


[ 사용자 정의 예외와 예외 발생 ]

애플리케이션 서비스와 관련된 예외를 애플리케이션 예외(Application Exception)이라고 한다. 애플리케이션 예외는 개발자가 직접 정의해서 만들어야 하므로 사용자 정의 예외라고도 한다.


1. 사용자 정의 예외 클래스 선언

사용자 정의 예외 클래스는 컴파일러가 체크하는 일반 예외로 선언할 수도 있고, 컴파일러가 체크하지 않는 실행 예외로 선언할 수도 있다.

  • 일반 예외로 선언할 경우 Exception을 상속하면 되고,
  • 실행 예외로 선언할 경우에는 RuntimeException을 상속하면 된다.
public class XXXException extends [ Exception | RuntimeException ] {
    public XXXException() { }
    public XXXException(String message) { super(message); }
}

사용자 정의 예외 클래스 이름은 Exception으로 끝나는 것이 좋다. 사용자 정의 예외 클래스도 필드, 생성자, 메소드 선언들을 포함할 수 있지만 대부분 생성자 선언만을 포함한다.

생성자는 두 개를 선언하는 것이 일반적인데

  • 하나는 매개 변수가 없는 기본 생성자이고,
  • 다른 하나는 예외 발생 원인(예외 메시지)을 전달하기 위해 String 타입의 매개 변수를 갖는 생성자이다.
    • String 타입의 매개 변수를 갖는 생성자는 상위 클래스의 생성자를 호출하여 예외 메시지를 넘겨준다.
    • 예외 메시지의 용도는 catch {} 블록의 예외 처리 코드에서 이용하기 위해서이다.

2. 예외 발생시키기

사용자 정의 예외 또는 자바 표준 예외를 코드에서 발생시키는 방법은 다음과 같다.

throw new XXXException();
throw new XXXException(“메시지”);

예외 객체를 생성할 때는 기본 생성자 또는 예외 메시지를 갖는 생성자 중 어떤 것을 사용해도 된다.

만약 catch 블록에서 예외 메시지가 필요하다면 예외 메시지를 갖는 생성자를 이용해야 한다. 예외 발생 코드를 가지고 있는 메소드는 내부에서 try-catch 블록으로 예외를 처리할 수 있지만, 대부분은 자신을 호출한 곳에서 예외를 처리하도록 throws 키워드로 예외를 떠넘긴다.

public void method() throws XXXException {
    throw new XXXException(“메시지”);
}

그렇기 때문에 throws 키워드를 포함하고 있는 메소드는 호출한 곳에서 다음과 같이 예외 처리를 해주어야 한다.

try {
    method();
} catch(XXXException e) {
    //예외 처리
}

[ 예외 정보 얻기 ]

try 블록에서 예외가 발생되면 예외 객체는 catch 블록의 매개 변수에서 참조하게 되므로 매개 변수를 이용하면 예외 객체의 정보를 알 수 있다.

모든 예외 객체는 Exception 클래스를 상속하기 때문에 Exception이 가지고 있는 메소드들을 모든 예외 객체에서 호출할 수 있다. 그중에서도 가장 많이 사용되는 메소드는 getMesage()printStackTrace()이다. 예외를 발생시킬 때 다음과 같이 String 타입의 메시지를 갖는 생성자를 이용했다면, 메시지는 자동적으로 예외 객체 내부에 저장된다.

throw new XXXException(“예외 메시지”);

예외 메시지의 내용에는 왜 예외가 발생했는지에 대한 간단한 설명이 포함된다. 좀 더 상세한 원인을 세분화하기 위해 예외 코드를 포함하기도 하는데, 예를 들어 데이터베이스에서 발생한 오류들은 예외 코드가 예외 메시지로 전달된다. 이와 같은 예외 메시지는 catch 블록에서 getMessage() 메소드의 리턴값으로 얻을 수 있다.

printStackTrace()는 메소드 이름에서도 알 수 있듯이 예외 발생 코드를 추적해서 모두 콘솔에 출력한다. 어떤 예외가 어디에서 발생했는지 상세하게 출력해주기 때문에 프로그램을 테스트하면서 오류를 찾을 때 활용된다.


[ 참고자료 ]

이것이 자바다 책
http://tcpschool.com/java/java_exception_throw

profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글