-
Item 9: Prefer try-with-resources to try-finally독서/Effective Java 2021. 12. 5. 06:30
try-finally보다는 try-with-resources를 사용하자
Java 라이브러리에는 close 메서드를 호출하여 수동으로 닫아야 하는 많은 자원들이 포함되어 있다.
close를 직접 호출하는 대표적인 예: InputStream, OutputStream, BufferedReader 등
만약 이걸 닫지 않는다면? 예측할 수 없을 정도로 심각한 성능 결과를 초래한다.
꼭 닫게 하기 위해 finalizer를 사용하는 경우도 있지만...(Item 8).
전통적으로 try-finally 문은 예외 또는 반환에 직면하더라도 리소스가 제대로 닫히도록 보장하는 가장 보편적인 방법이었다.
Try-Finally 사용
Try-Catch-Finally를 사용하는 가장 익숙한 예는 개인적으로는 JDBC이다.
public String getContactEmail(int contactNumber) throws SQLException { Connection connection = null; PreparedStatement doSelect = null; try { String contactEmail = null; String selectStatement = "SELECT contact_email FROM store WHERE contact_id = ?"; connection = ConnectionPool.getConnection(); doSelect = connection.prepareStatement(selectStatement); doSelect.setInt(1,contactNumber); ResultSet rs = doSelect.executeQuery(); if ( rs.next() ) { contactEmail = rs.getString(1); return contactEmail; } else { return null; } } catch (SQLException se) { // log the exception log.error(se); // re-throw the exception throw se; } finally { try { doSelect.close(); ConnectionPool.freeConnection(connection); } catch (Exception e) {} } } // from https://alvinalexander.com/blog/post/jdbc/-decent-example-of-using-try-catch-finally-with-jdbc/
이미 finally 문에서 connection을 닫기 위해 또 다른 try-catch 문을 사용한 것을 볼 수 있다. 그래도 하나의 자원을 사용한 것이기 때문에 그나마 try - finally에 대한 가독성이 어느 정도 존재한다.
만약 하나의 자원이 아닌 여러 자원이라면?
// try-finally is ugly when used with more than one resource! 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[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) { out.write(buf, 0, n); } } finally { out.close(); } } finally { in.close(); } }
일단 try 내에 또 try 문이 들어가 가독성을 해친다.
또한, 위의 JDBC의 예를 봤듯이 try, finally 모두 예외를 던질 수 있다(그렇기에 JDBC의 예에서는 try 시의 catch, finally 내에서의 try - catch도 구현이 되어 있다.)
만약 catch 없이 구현이 되었다면 finally에서의 예외가 try에서의 예외를 덮어버리는 상황이 발생할 수 있고, 그로 인해 개발자는 디버깅에 혼란을 겪을 수 있다.
그렇기에 Java 7에서부터는 새로운 방식의 자원 종료를 위한 방법이 도입되었다.
Try-with-Resources
새로운 방법은 try-with-resources[JLS, 14.20.3]를 사용하는 것이다.
전제 조건은 사용할 자원(JDBC Connector, InputStream 등)이 AutoCloseable 인터페이스를 구현해야 한다는 것밖에 없다.닫혀야 하는 리소스를 나타내는 클래스를 작성하는 경우 클래스도 AutoCloseable을 구현해야 한다.
더보기AutoCloseable?
https://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html
close로만 이루어진 인터페이스이다.
try-with-resources를 사용하면 다음과 같이 가능하다.
static String firstLineOfFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } }
이미 기본적으로 autoCloseable이 구현되어 있는 클래스로 해당 작업을 수행했기에 다음과 같이 비즈니스 로직만 볼 수 있게 된다.
여러 자원을 사용하는 경우도 다음과 같이 구현할 수 있다.
static void copy(String src, String dst) throws IOException { try (InputStream in = new FileInputStream(src); OutputStream 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 with a catch clause static String firstLineOfFile(String path, String defaultVal) { try (BufferedReader br = new BufferedReader( new FileReader(path))) { return br.readLine(); } catch (IOException e) { return defaultVal; } }
'독서 > Effective Java' 카테고리의 다른 글
Item 11: Always override hashCode when you override equals (0) 2021.12.12 Item 12: Always override toString (0) 2021.12.12 Item 8: Avoid finalizers and cleaners (0) 2021.12.05 Item 6: Avoid creating unnecessary objects (0) 2021.12.04 Item 5: Prefer dependency injection to hardwiring resources (0) 2021.12.04