Try-with-resources
는 아래의 코드와 같이 try에 자원 객체를 전달하면, try 코드 블록이 끝나면 자동으로 자원을 종료해주는 기능이다.
따라서, 따로 finally
블록이나 모든 catch
블록에 종료 처리를 하지 않아도 된다.
public void save(ChessBoard chessBoard) {
final var query = "INSERT chess_game (aa, bb) VALUES (?, ?)";
try (final var connection = getConnection();
final var preparedStatement = connection.prepareStatement(query)) {
preparedStatement.setString(1, "aaaa");
preparedStatement.setString(2, "bbbb");
preparedStatement.executeUpdate();
} catch (final SQLException e) {
throw new RuntimeException(e);
}
}
이때 try
에 전달할 수 있는 자원은 AutoCloseable
인터페이스의 구현체로 한정된다.
좀 더 자세히 알아보자!
자원을 회수하는 전통적인 방식
// path에 있는 파일을 열어서, 파일의 첫 줄을 가져오는 메서드
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
만약 어떠한 문제로 인해 (예를 들어, 파일을 열지 못하거나, 파일 입출력 시스템 자체의 물리적인 문제 등으로 인해)
br.readLine();
에서 IOException
이 발생하는 경우, br.close();
에서도 예외가 터지게 된다.
이 경우 스택 추적 내역에서 readLine()
에서 발생한 예외는 숨겨지고, br.close()
에서 발생한 예외만 기록되게 된다.
일반적으로 디버깅을 할 때는 처음 발생한 예외를 봐야하는데,
이 상황에서는 처음 발생한 예외가 숨겨지게 되어 디버깅이 어려워진다.
이걸 해결하려면
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
try {
br.close();
} catch (IOException e) {
// 예외 처리
}
}
}
이런 식으로 finally
문에도 try-catch
문을 넣어줘야 한다.
하지만, 이러면 코드가 너무 복잡해진다는 문제점이 있다!
이를 해결하기 위해 자바7에서 try-with-resources
가 생겨났다.
이 구조를 사용하려면 해당 자원이 AutoCloseable
인터페이스를 구현해야한다.
AutoCloseable
인터페이스는 void를 반환하는 close() 메서드 하나만 정의되어 있다.
public interface AutoCloseable {
void close() throws Exception;
}
자바 라이브러리와 서드파티 라이브러리들의 수많은 클래스와 인터페이스가 이미 AutoCloseable
을 구현하거나 확장해뒀기 때문에 우리가 직접 구현해야할 필요는 없다.
db를 적용할 때 사용하는 Connection
인터페이스를 보면, AutoCloseable
을 extends 하고 있는 것을 알 수 있다.
public interface Connection extends Wrapper, AutoCloseable {
Statement createStatement() throws SQLException;
PreparedStatement prepareStatement(String sql)throws SQLException;
...
}
try-with-resources
를 적용하면 다음과 같이 간단하게 나타낼 수 있다.
static String firstLineOfFile(String path) throws IOEXception {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
try
코드 블록이 끝나면 자동으로 clsoe 되기 때문에 자동으로 close된다. (finally문에 close를 위한 코드를 작성하지 않아도 된다.)
또, readLine()
에서 예외가 발생할 경우 readLine()
에서 발생한 예외가 기록된다.
만약 자원을 2개 사용한다면?
// src에 있는 파일에서 dst에 있는 파일로 내용을 복사하는 메서드
// FileInputStream : 파일로 부터 바이트로 입력받아, byte 단위로 출력할 수 있는 클래스
// FileOutputStream : byte단위로 파일을 기록하는 클래스 입니다.
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStrieam(dst);
try {
byte[] buf = new byte[BUFFER_SIZE]; // 읽어들인 데이터를 저장할 바이트 배열
int n;
while ((n = in.read(buf)) >= 0) { // in에서 buf의 크기만큼 데이터를 읽어들임. read()는 읽은 데이터의 크기를 반환하고, 읽을 데이터가 없으면 -1을 반환 (∴ 0 이상인지 비교)
out.write(buf, 0, n); // out에 buf에서 읽은 데이터를 쓴다. 시작위치 = 0, 바이트 수 = n
}
} finally {
out.close();
}
} finally {
in.close();
}
}
위 코드도 아래처럼 간결하게 나타낼 수 있다.
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);
}
}
}
'Back-End > JAVA' 카테고리의 다른 글
프로젝트에 Jacoco 적용하기 (2) | 2024.09.14 |
---|---|
Jacoco란? (0) | 2024.09.14 |