우주먼지
Published 2023. 3. 25. 07:19
Try-With-Resource Languages/Java

💡 Try-With-Resource

JDK 1.7 부터 try-catch의 변형인 try-with-resources가 추가되었다.

주로 입출력에 필요한 클래스들 중 사용 후 닫아줘야 하는것들을 자동으로 닫아준다.

그래야 사용했던 자원(resources)가 반환되기 때문이다.

 

아래 예시는 DataInputStream을 이용해 파일로부터 데이터를 읽는 코드이다.

데이터를 읽는 도중 예외가 발생하더라도 Stream이 닫히도록 finnaly 안에 close()를 넣었다.

 

별 문제가 없어 보이는 코드지만 진짜 문제는, close()가 예외를 발생시킬 수 있다는데 있다.

try {
    fis = new FileInputStream("score.dat");
    dis = new DataInputStream(fis);
} catch (IOException ie) {
    ie.printStackTrace();
} finally {
    dis.close();
}

 

아래 예시는 try-catch문을 추가해서 close()에서 발생할 수 있는 예외를 처리하도록 추가된 코드이다.

try {
    fis = new FileInputStream("score.dat");
    dis = new DataInputStream(fis);
} catch (IOException ie) {
    ie.printStackTrace();
} finally {
    try {
        if (dis != null) dis.close();
    } catch (IOException ie) {
        ie.printStackTrace();
    }
}

위 코드는 코드가 복잡해서 보기에 좋지 않고 더 나쁜 것은 try, finally에서 모두 예외가 발생하면,
try의 예외는 무시된다는 것이다.

이러한 점을 개선하기 위해 try-with-resource 문이 추가됬다.

 

이제 위의 코드를 try-with-resource문으로 바꿔보자.

try() 괄호 안에 객체를 생성하는 문장을 넣으면,
이 객체는 따로 close()를 해주지 않아도 try를 벗어나는 순간 자동으로 close()가 호출된다.

// 괄호 안에 두 문장 이상 넣을경우 ';'로 구분한다.
try (FileInputStream fis = new FileInputStream("score.dat");
    DataInputStream dis = new DataInputStream(fis)) {
    while (true) {
        score = dis.readInt();
        System.out.println(score);
        sum += score;
    } catch (EOFException e) {
        System.out.println("점수의 총합은 " + sum + "입니다.");
    } catch (IOException ie) {
        ie.printStackTrace();
    }
}

이처럼 try-with-resource문에 의해 자동으로 close()가 호출될 수 있으려면,

클래스가 AutoCloseable 인터페이스를 구현한 클래스여야만 한다.

 

그런데 위의 코드를 잘 보면 close()도 Exception을 발생시킬 수 있다.

 

만약 자동 호출된 close()에도 Exception이 발생하면 어떻게 될까?

예제를 먼저 실행시켜보자.

class TryWithResourceEx {
    public static void main(String[] args) {

        try (CloseableResource cr = new CloseableResource()) {
            cr.exceptionWork(false); // 예외가 발생하지 않는다.
        } catch (WorkException e) {
            e.printStackTrace();
        } catch (CloseException e) {
            e.printStackTrace();
        }
        System.out.println();


        try (CloseableResource cr = new CloseableResource()) {
            cr.exceptionWork(true); // 예외가 발생한다
        } catch (WorkException e) {
            e.printStackTrace();
        } catch (CloseException e) {
            e.printStackTrace();
        }
    }

    class CloseableResource implements AutoCloseable {
        public void exceptionWork(boolean exception) throws WorkException {
            System.out.println("exceptionWork("+exception+")가 호출됨");

            if (exception)
                throw new WorkException("Work Exception 발생!");
        }

        public void close() throws CloseException {
            System.out.println("close()가 호출됨");
            throw new CloseException("Close Exception 발생!");
        }
    }

    class WorkException extends Exception {
        WorkException(String msg) { super(msg); }
    }

    class CloseException extends Exception {
        CloseException(String msg) { super(msg); }
    }
}

/* 실행 결과
exceptionWork(false)가 호출됨
close()가 호출됨
CloseException: Close Exception 발생!
    at CloseableResource.close(TryWithResourceEX.java:33)
    at TryWithResourceEX.main(TryWithResource.java:6)

exceptionWork(true)가 호출됨
close()가 호출됨
WorkException: Work Exception 발생!
    at CloseableResource.exceptionWork(TryWithResourceEX.java:28)
    at TryWithResourceEx.main(TryWithResourceEX.java:15)
    Suppressed: CloseException: Close Exception 발생!
        at CloseableResource.close(TryWithResourceEX.java:33)
        at TryWithResourceEX.main(TryWithResourceEX.java:14)
*/

main 메서드에 두개의 try-catch문이 있다.

 

첫번쨰는 close()에서만 예외를 발생시킨다.

두번째는 exceptionWork()와 close() 에서 모두 예외를 발생시킨다.

 

첫번째는 일반적인 예외 발생할때와 같은 형태로 출력되었지만,
두번째는 출력형태가 다르다.

 

먼저 exceptionWork()에서 발생한 예외에 대한 내용이 출력되고,

close()에서 발생한 예죄는 '억제된(Suppressed)'라는 의미의 머리말과 함께 출력되었다.

 

두 예외가 동시에 발생할 수 없기에 실제 발생한 예외를 WorkException으로 하고,
CloseException은 억제된 예외로 다룬다.

 

억제된 예외에 대한 정보는 실제로 발생한 예외인 WorkException에 저장된다.

Throwable 클래스에는 억제된 예외와 관련된 메서드가 정의되어 있다.

void addSuppressed(Throwable exception) // 억제된 예외 추가
Throwable[] getSuppressed() // 억제된 예외(배열) 반환

만일 기존의 tyr-catch 문을 사용했다면,
먼저 발생한 WorkException은 무시되고 마지막 발생한 CloseException만 출력되었을 것이다.

'Languages > Java' 카테고리의 다른 글

Multi Catch Block  (0) 2023.03.25
Stream 생성  (0) 2023.03.17
Stream  (0) 2023.03.01
Single & Multi Thread  (0) 2023.03.01
Thread  (0) 2023.03.01
profile

우주먼지

@o귤o

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그