제육's 휘발성 코딩
article thumbnail
반응형

try-finally 보다는 try-with-resources를 사용하라

자바 라이브러리에는 InputStream, Connectionclose 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. 자원을 닫는 것은 놓치기 쉬워 예측하기 어려운 성능 문제로 이어지기도 한다. 상당 수가 안정망으로 finalizer, cleaner 등을 활용하지만 실행을 보장하지 못한다.

 

예외가 발생하거나 메서드에서 반환되는 경우를 포함해서 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰였다. 다음 코드 예시를 살펴보자.

try-finally

static String firstLineOfFile(String path) throws IOException {
  BufferedReader br = new BufferedReader(new FileReader(path));
  try {
    br.readLine();
  } finally {
    br.close();
  }
  return "";
}

static void copy(String src, String dst) throws IOException {
  InputStream in = new FileInputStream(src);
  try {
    OutputStream out = new FileOutputStream(dst);
    try {
      byte[] buf = new byte[1024];
      int n;
      while ((n = in.read(buf)) >= 0)
        out.write(buf, 0, n);
    } finally {
      out.close();
    }
  } finally {
    in.close();
  }
}
  • 다음과 같이 finally 를 통해 예외 또는 메서드 반환의 경우에도 close를 할 수 있다.
  • 물리적인 문제 등이 생겨서 br 과 관련된 기능들이 예외를 발생시킨다면, 현재 코드에선 br.readLine()br.close() 에서 예외가 발생된다. finally 블록은 try 블록에서 예외가 발생해도 실행하기 때문에 close에서 발생한 예외는 기록되지 않고, readLine에서 발생한 예외만 기록된다. 즉, 첫 번째 예외에 대한 정보는 남지않기 때문에 디버깅이 어려워 진다.

try-with-resources

try-finally 구문의 문제점을 해결하기 위해 try-with-resources 구문이 자바 7 버전부터 지원되었다. 단, 이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야 한다. 즉, 반드시 자원을 반환해야하는 경우 해당 인터페이스를 구현 또는 확장해야 한다.

해당 구문을 사용하면 readLine(), close() 양 쪽에서 예외가 발생해도 모든 예외가 출력된다.

static String firstLineOfFile(String path) throws IOException {
  try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    br.readLine();
  }
  return "";
}

static void copy(String src, String dst) throws IOException {
  try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst)) {
    byte[] buf = new byte[1024];
    int n;
    while ((n = in.read(buf)) >= 0)
      out.write(buf, 0, n);
  }
}
  • 코드가 간결해지고, 이중 try 구문도 가독성이 좋아졌다. 하지만, AutoCloseable 구현이 없으므로 해당 기능으론 close에 대한 보장이 되지않는다.
  • finally와의 차이점은 try 안에 파라미터로 자원을 넘겨주는 것이다. 또한 try 구문안에 여러 자원을 넣을 수 있다. 마찬가지로 catch 문도 사용이 가능하다.

 

package effective.item9;

import java.io.*;

public class CloseTest implements AutoCloseable {

  static String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
      br.readLine();
    }
    return "";
  }

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

  public void doSomething() {
    System.out.println("Something");
  }
  @Override
  public void close() throws Exception {
    System.out.println("closed");
  }
}

/*
    출력 
    Something
    closed
*/
class CloseMainTest {
  public static void main(String[] args) {
    try (CloseTest ct = new CloseTest()) {
      ct.doSomething();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
  • 다음과 같이 AutoCloseable 인터페이스를 구현하여 close를 재정의 한다면 자동으로 반환한다.

 

결론

회수해야 하는 자원을 다루는 모든 경우에서 try-finally가 아닌 try-with-resources를 사용하자. 코드가 더 짧아지고 가독성이 높아지며, 정확하고 쉽게 자원을 회수할 수 있다. 또한 close를 보장하고 싶은 경우 Closeable 인터페이스를 구현하자.


REFERENCES

https://www.baeldung.com/java-try-with-resources

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^