Computer Science/Java

try-with-resources

eunnnn 2023. 4. 17. 11:13

Resource

자원은 시스템을 운영하는 데 있어서 메모리나 입출력 장치 등 하드웨어, 소프트웨어 형태로 존재하는 구성요소를 의미한다. 그러나 보통 resource란 외부의 데이터(DB, Network, File)를 일컫는 말로 한정되어 쓰인다.

이런 resource들은 자바 내부에 위치한 요소들이 아니기 때문에, 이러한 프로세스 외부에 있는 데이터를 자바 코드에서 접근하려고 할 때 문제(예외)가 발생할 수 있는 여지가 존재한다.

특히나 이런 입출력에 관련된 resource들에 접근해서 사용하고 나면 닫는 것이 굉장히 중요하다. 예를들어 파일에 접근해 파일을 열고 내용을 쓰는 중에 다른곳에서 같은 resource에 접근해 사용하다 보면 데이터가 꼬일 수 있기 때문이다.

GC(Garbage Collection) 라는 자동으로 관련 메모리가 해제되는 기능이 있만 GC 는 메모리를 해제하는 시간을 예측하기 힘들기 때문에 할당받은 자원의 해제 시점 또한 예측하기가 힘들다. 따라서 GC 에서 해제하는 것을 기다리기보다는, 자원에 대한 사용이 끝나면 직접 해제해주는 편이 좋다.

 

Java 7 이전의 try-catch-finally

사용 후에 반납해주어야 하는 자원들은 Closable 인터페이스를 구현하고 있으며, 사용 후에 close 메소드를 호출해주어야 했다.

Java7 이전에는 close를 호출하기 위해서 try-catch-finally를 이용해서 Null 검사와 함께 직접 호출해야 했는데, 대표적으로 파일의 내용을 읽는 경우를 다음과 같이 구현할 수 있었다.

public static void main(String args[]) throws IOException {
    FileInputStream is = null;
    BufferedInputStream bis = null;
    try {
        is = new FileInputStream("file.txt");
        bis = new BufferedInputStream(is);
        int data = -1;
        while((data = bis.read()) != -1){
            System.out.print((char) data);
        }
    } finally {
        // close resources
        if (is != null) is.close();
        if (bis != null) bis.close();
    }
}

문제는 이러한 과정이 여러가지 단점을 가지고 있다는 것이다.

  • 자원 반납에 의해 코드가 복잡해짐
  • 작업이 번거로움
  • 실수로 자원을 반납하지 못하는 경우 발생
  • 에러로 자원을 반납하지 못하는 경우 발생
  • 에러 스택 트레이스가 누락되어 디버깅이 어려움

그래서 이러한 문제를 해결하기 위해 try-with-resources라는 문법이 Java7부터 추가되었다.

 

Java 7 부터의 try-with-resources

JDK1.7부터 try - with - resources 문이라는 try - catch 문의 변형 문법이 새로 추가되었다.

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

 try 블록에 괄호()를 추가하여 파일을 열거나 자원을 할당하는 명령문을 명시하면, 해당 try 블록이 끝나자마자 자동으로 파일을 닫거나 할당된 자원을 해제해 준다.

 

즉 try - with - resources 문의 괄호() 안에 객체를 생성하는 문장을 넣으면, 따로 close() 를 호출하지 않아도 try 블럭을 벗어나는 순간 자동적으로 close()가 호출된다는 것이다. 그리고 다음에 catch 블럭 또는 finally 블럭이 수행된다

import java.io.FileWriter;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (FileWriter file = new FileWriter("data.txt")) { // 파일을 열고 모두 사용되면 자동으로 닫아준다
            file.write("Hello World");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

AutoCloseable 인터페이스

Java는 AutoCloseable 인터페이스를 구현하고 있는 자원에 대해 try-with-resources를 적용 가능하도록 하였다.

한 가지 재밌는 사실은, 여기서 기존의 Closeable에 부모 인터페이스로 AutoCloesable을 추가했기 때문에 기존에 구현된 자원 클래스들 모두 try-with-resources가 사용가능해졌다.

상식적으로 먼저 만들어진 인터페이스를 자식 클래스나 인터페이스에 사용하는 것이 일반적일 것이다. 하지만 Java 개발자들은 먼저 만들어진 Cloesable 인터페이스에 부모 인터페이스인 AutoCloesable을 추가함으로써 하위 호환성을 100% 달성함과 동시에 변경 작업에 대한 수고를 덜었다. 

 

 

 

출처