Effective Java : 아이템9. 완벽공략

    들어가기 전

    이 글은 백기선님의 이펙티브 자바 강의를 복습하며 작성한 글입니다.


    아이템 9.완벽 공략

    • p48, 자바 퍼즐러 예외 처리 코드의 실수
    • p49, try-with-resources 바이트 코드 

    이 장에서는 위 두 가지에 대한 완벽 공략을 공부했다. 

     


    자바 퍼즐러

    자바 퍼즐러는 책이다. 자바 퍼즐러에는 아래와 같은 코드가 있는데, 이렇게 작성된 코드는 안전한 코드인지 아닌지에 대해서 많은 사람들이 헷갈려한다. 이 부분은 자원을 중첩으로 사용하고 해제하는데 있어서 try - finally 문의 약점을 보여준다.

    이렇게 했을 때 안전한 코드일까? 이 코드는 안전하지 않은 코드다. 왜냐하면 다음 상황이 발생할 수 있기 때문이다.

    out.close()를 하다가 발생하는 에러는 catch 절에서 IOException만 잡는다. 만약 out.close()에서 RuntimeException이 발생하게 된다면 뒷쪽에 있는 코드 in.close() 는 수행되지 않는다. 

    만약 try - finally를 이용해서 안전하게 자원을 종료하고 싶다면 아래 코드처럼 nested로 작성해주어야 한다. 아래 코드에서는 out.close() 실행 도중 예상치 못한 에러가 나와도 outer의 finally 절로 넘어가고, 이곳에서 in.close()를 정상적으로 호출해 줄 수 있게 된다.

    static void copy(String src, String dst) throws IOException {
        FileInputStream in = new FileInputStream(src);
        try {
            FileOutputStream out = new FileOutputStream(dst);
            try {
                byte[] buf = new byte[BUFFER_SIZE];
                int n;
                while ((n = in.read(buf)) >= 0)
                    out.write(buf, 0, n);
            }finally {
                out.close();
            }
        }finally {
            in.close();
        }
    }

    가장 좋은 방법은 try-with-resources 구문을 사용하는 것이다. 아래처럼 깔끔하게 고쳐서 사용할 수 있게 된다. 

    static void copy(String src, String dst) throws IOException {
        try(FileInputStream in = new FileInputStream(src);
            FileOutputStream out = new FileOutputStream(dst)){
            
            byte[] buf = new byte[BUFFER_SIZE];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        }
    }

     


    try-with-resources 바이트 코드 

    그렇다면 try-with-resource는 어떻게 구성되어 있길래, 자동으로 close()가 호출되고 예외가 보존되어서 나오는 것일까? try-with-resource는 build를 하게 되면 특정한 바이트 코드로 바뀌게 되는데, 이것이 이런 기능들을 구현해준다. 다음 순서를 따른 후에 바이트 코드를 살펴본다.

    • gradle clean
    • gradle build

    위 명령어를 수행하면 Target 디렉토리에 생성된 바이트 코드를 살펴볼 수 있다. 코드는 어떻게 바뀌었을까? 아래는 기본적으로 작성한 자바코드다. 이 자바 코드가 바이트 코드로 바뀐 것을 살펴보자.

    바뀐 자바코드는 다음과 같다. 여기서 살펴볼 점은 var5 + addSuppressed() 메서드다.

    • var5는 가장 첫번째 발생한 에러를 catch 절에서 잡은 것이다.
    • var4는 두번째 발생한 에러다. 두번째 발생한 에러는 var5.addSuppressed()를 통해서 추가된다. 


    정리

    • try - finally로 중첩 자원을 정리한다면, nested로 정리하는 것이 가장 안전하다. (try-with-resources를 사용하라) 
    • 첫번째 발생한 에러가 다른 에러에 의해서 없어지는 것을 원하지 않는다면, 첫번째 발생한 에러에 addSuppressed()를 이용해서 에러를 추가 구성하는 형태로 작성한다. 

    댓글

    Designed by JB FACTORY