티스토리 뷰
try-with-resources란?
자바에서 자원을 사용하는 블록을 처리할 때 자동으로 자원을 해제하는데 사용되는 구문으로 사용 방법은 아래와 같습니다.
try (Resource resource = new Resource()) {
// 자원 사용 코드
} catch (Exception e) {
// 예외 처리 코드
}
이 때 try의 매개변수에서 선언할 수 있으려면 AutoClosable
인터페이스를 구현해야 합니다. 대부분의 자바 표준 라이브러리 클래스들은 AutoClosable
인터페이스를 구현하고 있습니다. 커스텀 클래스를 사용하려면 해당 클래스가 AutoClosable
인터페이스를 구현해야 합니다.
AutoClosable 펼쳐 보기
/*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
/**
* An object that may hold resources (such as file or socket handles)
* until it is closed. The {@link #close()} method of an {@code AutoCloseable}
* object is called automatically when exiting a {@code
* try}-with-resources block for which the object has been declared in
* the resource specification header. This construction ensures prompt
* release, avoiding resource exhaustion exceptions and errors that
* may otherwise occur.
*
* @apiNote
* <p>It is possible, and in fact common, for a base class to
* implement AutoCloseable even though not all of its subclasses or
* instances will hold releasable resources. For code that must operate
* in complete generality, or when it is known that the {@code AutoCloseable}
* instance requires resource release, it is recommended to use {@code
* try}-with-resources constructions. However, when using facilities such as
* {@link java.util.stream.Stream} that support both I/O-based and
* non-I/O-based forms, {@code try}-with-resources blocks are in
* general unnecessary when using non-I/O-based forms.
*
* @author Josh Bloch
* @since 1.7
*/
public interface AutoCloseable {
/**
* Closes this resource, relinquishing any underlying resources.
* This method is invoked automatically on objects managed by the
* {@code try}-with-resources statement.
*
* <p>While this interface method is declared to throw {@code
* Exception}, implementers are <em>strongly</em> encouraged to
* declare concrete implementations of the {@code close} method to
* throw more specific exceptions, or to throw no exception at all
* if the close operation cannot fail.
*
* <p> Cases where the close operation may fail require careful
* attention by implementers. It is strongly advised to relinquish
* the underlying resources and to internally <em>mark</em> the
* resource as closed, prior to throwing the exception. The {@code
* close} method is unlikely to be invoked more than once and so
* this ensures that the resources are released in a timely manner.
* Furthermore it reduces problems that could arise when the resource
* wraps, or is wrapped, by another resource.
*
* <p><em>Implementers of this interface are also strongly advised
* to not have the {@code close} method throw {@link
* InterruptedException}.</em>
*
* This exception interacts with a thread's interrupted status,
* and runtime misbehavior is likely to occur if an {@code
* InterruptedException} is {@linkplain Throwable#addSuppressed
* suppressed}.
*
* More generally, if it would cause problems for an
* exception to be suppressed, the {@code AutoCloseable.close}
* method should not throw it.
*
* <p>Note that unlike the {@link java.io.Closeable#close close}
* method of {@link java.io.Closeable}, this {@code close} method
* is <em>not</em> required to be idempotent. In other words,
* calling this {@code close} method more than once may have some
* visible side effect, unlike {@code Closeable.close} which is
* required to have no effect if called more than once.
*
* However, implementers of this interface are strongly encouraged
* to make their {@code close} methods idempotent.
*
* @throws Exception if this resource cannot be closed
*/
void close() throws Exception;
}
그리고 전달할 자원은 반드시 final
또는 effectively final
(자바 8 이상에서 도입된 개념으로, 변수가 사실상 final으로 동작하는 경우를 말함) 변수여야 합니다. try
블록이 실행되는 동안만 유효해야하기 때문입니다.
마지막으로 자원은 단일 문장으로 초기화되어야 합니다.
try (String path = "build.gradle";
BufferedReader bufferedReader = new BufferedReader(new FileReader(path))) {
return bufferedReader.readLine();
}
이런식의 소스 코드는 사용할 수 없고,
try (BufferedReader bufferedReader = new BufferedReader(new FileReader("build.gradle"))) {
return bufferedReader.readLine();
}
이런식으로 한 문장으로 선언해야 합니다.
try-with-resources
구문을 사용하면 코드가 더 짧고 분명해지고 만들어지는 예외 정보가 훨씬 유용하다는 장점이 있습니다.
try-with-resources 구문을 사용했을 때 실제 코드는 어떻게 작성될까?
위에 작성했던 예시 코드
try (Resource resource = new Resource()) {
// 자원 사용 코드
} catch (Exception e) {
// 예외 처리 코드
}
를 바이트 코드로 변환하면 다음과 같습니다.
Resource resource = new Resource();
Throwable primaryException = null;
try {
// 자원 사용 코드
} catch (Throwable e) {
primaryException = e;
throw e;
} finally {
if (resource != null) {
if (primaryException != null) {
try {
resource.close();
} catch (Throwable suppressedException) {
primaryException.addSuppressed(suppressedException);
}
} else {
resource.close();
}
}
}
위의 변환된 바이트코드는 try
블록에서 자원을 사용하고, catch
블록에서 예외를 처리하며, finally
블록에서 자원을 해제하는 로직을 보여줍니다. finally
블록에서 자원을 해제하는 부분은 close()
메서드를 호출하여 자원을 해제하는 코드로 변환됩니다.
특이한 점은 첫 번째 예외가 발생했을 때 catch
블럭에서 미리 선언한 변수에 할당하고 다시 던져주고 finally
블럭에서 추가로 발생할 수 있는 예외를 첫 번째 예외의 suppressed
로 등록해준다는 점입니다.
이렇게 처리하면 예외 로그가 보다 더 정확히 남는 것을 확인할 수 있습니다.
suppressed는 주 예외 처리 중에 발생한 부가적인 예외들을 의미합니다.
그래서 예외 정보가 어떻게 유용한 걸까?
예시 코드로 확인해보면,
class Resource implements AutoCloseable {
@Override
public void close() throws IOException {
// 호출되면 내용이 서로 다른 예외를 발생시킴
throw new IOException("Closing Resource Failed");
}
public void readData() throws IOException {
// 호출되면 내용이 서로 다른 예외를 발생시킴
throw new IOException("Reading Data Failed");
}
}
public class TryWithResourceExceptionTest {
public static void main(String[] args) throws IOException {
try (Resource resource = new Resource()) {
resource.readData();
}
}
}
이 예시를 실행하면 무조건 예외가 발생하게 되는데 다음과 같은 에러 로그를 확인할 수 있습니다.
Exception in thread "main" java.io.IOException: Reading Data Failed
at Resource.readData(scratch_82.java:16)
at TryWithResourceExceptionTest.main(scratch_82.java:23)
Suppressed: java.io.IOException: Closing Resource Failed
at Resource.close(scratch_82.java:12)
at TryWithResourceExceptionTest.main(scratch_82.java:22)
예외가 발생한 순서대로 예외 로그를 표현하는데, 이후에 발생한 로그가 Suppressed
에 명시된 것을 확인할 수 있습니다.
try-catch-finally
에서 처리하게 되면 마지막에 발생한 예외만 표시해주기 때문에 정확히 어떤 부분에서 어떤 에러가 발생했는지 알기 어렵습니다. 특히 catch
블럭에서 상세한 예외를 처리하도록 해두었다면 그 외의 에러가 발생하여 제대로 수행되지 않았을 때도 해당 내용을 추적하기 어렵습니다. 이를 방지하기 위해 더 추상화된 레벨의 예외만 처리해도 마찬가지로 정확한 예외를 알기 어렵습니다.
한 줄 요약: try-catch-finally 대신 try-with-resources 씁시다!
당연히 try-with-resources에도 catch 블럭을 사용할 수 있습니다. 예시를 극단적으로 보여드리기위해 catch 블럭을 제외시켰으나 예외처리가 필요하다면 catch 블럭을 작성해야 합니다.
'Java' 카테고리의 다른 글
삼쩜삼에서 부동소수점을 다루는 방법 (2) | 2024.09.23 |
---|---|
[Java] PhantomReference를 활용해 가비지 컬렉션 리소스 해제 감지하기 (0) | 2023.05.11 |
[Java] WeakReference와 Garbage Collector (0) | 2023.05.10 |
Heap Pollution (0) | 2023.03.29 |
Unchecked Exception에 관한 논쟁 (0) | 2023.03.28 |
- Total
- Today
- Yesterday
- Spring Data JPA
- Spring Boot JPA
- 스프링 데이터 jpa
- intellij
- JPA
- Jackson
- spring boot application
- 헥사고날 아키텍처
- 스프링부트
- 스프링 부트
- spring boot app
- 함께 자라기 후기
- 스프링 부트 튜토리얼
- Spring Boot Tutorial
- JSON
- 스프링 부트 애플리케이션
- spring boot jwt
- Linux
- @ManyToOne
- Spring Boot
- leetcode
- 스프링 부트 회원 가입
- 함께 자라기
- Java
- proto3
- QueryDSL
- 알고리즘
- r
- 클린 아키텍처
- gRPC
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |