Skip to content

Latest commit

 

History

History
162 lines (130 loc) · 5.33 KB

Try~with~resources.md

File metadata and controls

162 lines (130 loc) · 5.33 KB

자동 자원 반환 - try - with - resources


JDK1.7부터 try-with-resources 이라는 try-catch문의 변형이 새로 추가되었다. 주로 입출력에 사용되는 클래스 중에서는 사용한 후에 꼭 닫아 줘야 하는 것들이 있다. 그래야 사용했던 자원(resources)이 반환되기 때문이다.

class test {
    public void tryCatch() {
        try {
            fis = new FileInputStream("score.dat");
            dis = new DataInputStream(fis);
        } catch (IOException io) {
            ie.printStackTrace();
        } finally {
            dis.close();
        }
    }   
}

위의 코드는 DataInputStream을 사용해서 파일로부터 데이터를 읽는 코드인데, 데이터를 읽는 도중에 예외가 발생하더라도 DataInputStream이 닫히도록 finally 블럭 안에 close()를 넣었다. 여기까지는 문제가 없어보이지만 진짜 문제는 close()가 예외를 발생시킬 수 있다는데 있다


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

finally 블록안에 try-catch 추가해서 close()에서 발생할 수 있는 예외를 처리하도록 변경했는데, 코드가 복잡해져서 보기가 좋지 않다. 더 나쁜 것은 try블럭과 finally블럭에서 모두 예외가 발생하면, try블럭의 예외는 무시된다는 것이다. 한마디로 두개의 에러가 모두 발생할 수는 없으니까 뒤에 발생한 에러만 발생하게 된다. 이러한 점을 개선하기 위해 try-with-resources이 추가되었다. 위의 코들르 try-with-resources로 바꾸면 아래와 같다.

class test {
    public void tryCatch() {
        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-resources문의 괄호()안에 객체를 생성하는 문장을 넣으면, 이 객체는 따로 close()를 호출하지 않아도 try 블록을 벗어나는 순간 자동적으로 close()가 호출된다. 그 다음에 catch 블 또는 finally 블럭이 수행된다.


이처럼 try-with-resources문에 의해 자동으로 객체의 close()가 호출될 수 있으려면, 클래스가 AutoCloseable이라는 인터페이스를 구현한 것이어야 한다.

class test {
    public interface AutoCloseable {
        void close() throws Exception;
    }     
}

예제

class test {
    public static void main() {
        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("WorkException발생!!!");
        }
    }   
    
    public void close() throws CloseException {
        System.out.println("close()가 호출됨");
        throw new CloseException("CloseException발생!!");
    }
}

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

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

main메소드에 두 개의 try-catch문이 있는데, 첫 번째 것은 close()에서만 예외를 발생시키고, 두 번째 것은 exceptionWork()와 close()에서 모두 예외를 발생시킨다.


첫 번째는 일반적인 예외가 발생했을 때와 같은 형태로 출력되었지만, 두 번째는 출력형태가 다르다. 먼저 exceptionWork()에서 발생한 예외에 대한 내용이 출력되고, close()에서 발생한 예외는 억제된(suppressed)이라는 것이 출력된다. 두 예외가 동시에 발생할 수는 없기 때문에, 실제 발생한 예외를 WorkException 으로 하고, CloseException은 억제된 예외로 다룬다.


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