일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Spring Cloud OpenFeign
- Spring Reactive Programming
- 코드로 배우는 스프링 부트 웹 프로젝트
- 멀티프로세싱
- 네이버클라우드 서버
- springboot
- spring boot
- 도메인 주도 설계(DDD) 기반 마이크로서비스(MSA) 모델링
- 2024년 상반기 회고
- 비동기
- Spring Security
- 비사이드프로젝트
- 오블완
- 스레드
- 멀티태스킹
- 사이드 프로젝트
- 티스토리챌린지
- Apple 로그인
- 멀티스레드
- ExceptionHandlerFilter
- querydsl
- asciidoctor
- microsoft
- ExecutorService
- FeignClients
- OAuth2.0
- REDIS
- 프로세스
- JWT
- OpenFeign
- Today
- Total
기록하기
try-with-resources 사용법 예시와 AutoCloseable 구현 본문
File I/O
자바의 I/O 는 기본적으로 InputStream, OutputStream 이라는 abstract 클래스가 제공된다.
어떤 파일을 읽을 때 InputStream 의 자식 클래스로 읽고, 데이터를 쓸 때에는 OutputStream 의 자식 클래스로 쓰면 된다.
이때 꼭 기억해야 하는 메소드는 read(), close() 이며 특히 close() 의 경우 리소스를 닫을 때 사용하는 메소드로 매우 중요하다. 결국 '자원'을 쓰는 것이므로 close 를 해줘야 하며 try-catch-finally 에서는 finally 에서 close() 처리를 해줄 수 있었다.
test.txt
Java File I/O test file
try-catch-finally
public class TryCatchExample {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = null;
BufferedInputStream bufferedInputStream = null;
try {
fileInputStream = new FileInputStream("test.txt");
bufferedInputStream = new BufferedInputStream(fileInputStream);
int i = -1;
while ((i = bufferedInputStream.read()) != -1) {
System.out.print((char) i);
}
} catch (IOException e) {
//log.error(e);
} finally {
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
if (fileInputStream != null) {
fileInputStream.close();
}
}
}
}
그런데 이 경우 아래와 같은 단점이 있다.
- 유한한 자원인 FileDescriptor 를 할당 받아서 처리하다보니 자원을 선점한 뒤 close 를 하지 않으면 결국 memory leak 이 발생하게 된다.
- 또한 ouput 의 경우에는 buffer 가 있는데 어느 정도 채워진 상태에서 close 를 안 하게 되면 flush 를 하지 않아 기록이 안 되는 문제가 발생할 수도 있다.
즉, close() 를 호출하여 닫아줘야 하는데,
- try-catch-finally 에서는 null 체크 후에 직접 호출을 해줘야하는 문제도 있고, 실수 혹은 에러로 인해 자원이 반납되지 않을 수 있다.
- 그리고, InputStream, OutputStream 둘 다 계층구조라서 하위 것을 안 닫고 중간 것을 닫으면 문제가 발생한다.
이런 단점을 해결하고자 Java7 에서는 try-with-resources 가 추가되었는데, try-with-resources 에 대해서 간단하게 살펴보고 AutoCloseable 구현 클래스를 생성해보도록 하겠다.
try-with-resources
try-with-resources 사용방법은 아래와 같다.
public class TryWithResourcesExample {
public static void main(String[] args) {
try (FileInputStream fileInputStream = new FileInputStream("test.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)
) {
int i = -1;
while ((i = bufferedInputStream.read()) != -1) {
System.out.print((char) i);
}
} catch (IOException e) {
//log.error(e);
}
}
}
try 블록에 괄호를 추가해서 파일 input 과 관련된 명령어를 작성하면, try 블록이 끝날 때 자동으로 파일을 닫거나 자원을 해제해준다.
하지만, 한 가지 주의해야할 것은, try-with-resources 를 다 사용할 수 있는 것이 아니라 AutoCloseable 을 구현하고 있는 자원 클래스들에서 사용할 수 있다는 점이다.
만약 그렇지 않은 경우에서 사용하고 싶다면 직접 구현하는 클래스를 설계해줘야하는데 예시는 다음과 같다.
CustomResource
public class CustomResource implements AutoCloseable {
public void doSomething() {
System.out.println("doSomething");
}
@Override
public void close() throws Exception {
System.out.println("close");
}
}
구현 클래스에서는 close() 메소드를 꼭 override 하여 재정의를 해줘야 한다. 간단하게 close 문구를 출력해봄으로써 동작 여부를 확인할 수 있다.
try-with-resources
public class TryWithResourcesExample {
public static void main(String[] args) {
try (CustomResource customResource = new CustomResource()) {
customResource.doSomething();
} catch (IOException e) {
//log.error(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
실제로 실행을 해보면, 아래와 같이 출력된다.
한 가지 더 확인을 해보고 싶은 게 있는데 try-with-resources 를 사용하게 되면 기존에 try-catch-finally 에서 발생한 에러 스택 트레이스가 누락되는 문제를 해결할 수 있다고 하여 에러를 발생시켜서 확인을 해보려고 한다.
먼저, try-catch-finally 와 CustomResource 를 활용하여 에러를 발생시켜보자
CustomResource
public class CustomResource implements AutoCloseable {
public void doSomething() {
System.out.println("doSomething");
throw new IllegalStateException(); //추가
}
@Override
public void close() throws Exception {
System.out.println("close");
throw new IllegalStateException(); //추가
}
}
try-catch-finally
public class TryCatchExample {
public static void main(String[] args) throws Exception {
CustomResource customResource = null;
try {
customResource = new CustomResource();
customResource.doSomething();
} finally {
if (customResource != null) {
customResource.close();
}
}
}
}
일부로 doSomething(), close() 두 곳에서 exception 을 발생시켰기 때문에 에러 스택 트레이스는 이 두 곳이 모두 찍혀야 한다. 하지만 결과는..
close() 에서 발생한 에러만 찍히는 것을 알 수 있다. 이 경우 doSomething() 에서 생긴 에러는 찍히지 않아, 원인 파악이 힘들어질 것이다.
만약 try-with-resources 를 활용하게 되면 어떻게 될까?
try-with-resources
이와 같이 doSomething, close 메소드에서 발생한 모든 에러가 찍히는 것을 확인할 수 있고, 이를 통해 에러 원인 파악면에서도 try-with-resources 를 사용해야함을 알 수 있다.
자원 해제 및 반납, 에러 스택 트레이스 등의 관점에서 볼 때 try-catch-finally 가 아닌 try-with-resources 를 활용해야함을 알 수 있었고, AutoCloseable 을 직접 구현하여 사용할 수 있음을 알게 되었다.
'language > java' 카테고리의 다른 글
프로세스와 스레드, 멀티태스킹 (1) | 2024.11.18 |
---|---|
String Constant Pool 과 equals, hashCode (0) | 2023.09.07 |
Java의 GC 진행 방법 (0) | 2023.08.18 |
List, Set, Map 의 차이 (0) | 2022.06.11 |