Quick Flow
Path path = Path.of("notes.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
System.out.println(reader.readLine());
}try (...) 안에서 연 AutoCloseable 자원은 block을 벗날 때 역순으로 닫힙니다. 본문과 close()가 함께 실패하면 본문 예외가 유지되고 close 예외는 suppressed exception이 되므로, 파일·소켓·DB 연결은 수동 finally보다 이 형태를 기본으로 둡니다.
문법
어떤 파일 처리 흐름을 먼저 떠올리면 되나
| 상황 | 먼저 떠올릴 것 |
|---|---|
| 외부 자원 열기 | try-with-resources |
| 파일 전체 읽기 | Files.readString() |
| 줄 단위 읽기 | Files.readAllLines() 또는 newBufferedReader() |
| 파일 쓰기 | Files.writeString() |
| 존재 여부 확인 | Files.exists() |
자원 수명주기: 열었으면 닫는 경로가 있어야 한다
파일, 네트워크 소켓, DB 연결처럼 외부 자원을 열면 닫는 경로가 있어야 합니다. 닫지 않으면 파일 디스크립터나 커넥션이 누수되어 시스템 자원이 소진될 수 있습니다. 예외가 발생해도 닫기가 실행되도록 구조를 잡는 점이 핵심입니다.
// ❌ 수동 닫기 — 예외 발생 시 close()가 호출되지 않을 수 있음
BufferedReader reader = Files.newBufferedReader(path);
String line = reader.readLine(); // 여기서 예외 발생하면 close() 미호출
reader.close();try-with-resources: 자동 닫기 보장
try-with-resources는 AutoCloseable을 구현한 자원을 블록 끝에서 닫도록 언어 차원에서 지원합니다. 예외가 발생해도, 정상 종료되어도 close()가 호출됩니다. 수동 finally보다 코드가 짧고 누락 가능성도 줄어듭니다.
// ✅ try-with-resources — 예외 여부와 관계없이 자동 닫기
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} // 블록 끝에서 reader.close() 자동 호출
// 여러 자원도 처리 가능 — 역순으로 닫힘
try (var conn = dataSource.getConnection();
var stmt = conn.prepareStatement(sql)) {
// ...
}여러 resource는 선언의 역순으로 닫힙니다. try 본문 예외가 primary exception이고, close 중 난 예외는 getSuppressed()에서 확인할 수 있습니다. close failure를 잃지 않기 위해 수동 finally에서 원래 예외를 덮어쓰지 않습니다.
Files 고수준 API 활용
현대 Java 파일 처리는 java.nio.file.Files를 중심으로 이해하는 편이 좋습니다. 상황에 맞는 고수준 메서드를 먼저 떠올리면 코드를 간결하게 만들 수 있습니다.
Path path = Path.of("data.txt");
// 파일 전체를 한 번에 읽기
String content = Files.readString(path);
// 줄 단위로 읽기
List<String> lines = Files.readAllLines(path);
// 파일 쓰기
Files.writeString(path, "Hello\nWorld", StandardOpenOption.CREATE);
// 파일 존재 여부
if (Files.exists(path)) { ... }체크포인트
| 상황 | 적합한 선택 |
|---|---|
| 외부 자원 사용 | try-with-resources |
| 파일 전체 읽기 | Files.readString(path) |
| 줄 단위 읽기 | Files.readAllLines(path) |
| 파일 쓰기 | Files.writeString(path, content) |
| 스트림 방식 읽기 | Files.newBufferedReader(path) |
텍스트 I/O는 charset을 명시하는 편이 portable합니다. overwrite, append, create-new, atomic replace는 모두 다른 write policy이므로 StandardOpenOption과 Files.move(..., ATOMIC_MOVE) 지원 여부를 결과 요구사항에 맞춰 고릅니다.
주의할 점
수동으로 finally에서 닫으면 예외가 겹칠 때 원래 예외와 닫기 예외의 관계를 다루기 번거롭습니다. try-with-resources는 이런 경우 닫기 예외를 suppressed exception으로 연결해 추적하기 쉽게 만듭니다.
// ❌ 수동 finally — close()에서 예외 발생 시 원래 예외가 묻힘
BufferedReader reader = null;
try {
reader = Files.newBufferedReader(path);
process(reader.readLine());
} finally {
if (reader != null) reader.close(); // close() 예외가 원래 예외를 덮음
}
// ✅ try-with-resources — suppressed exception으로 안전하게 처리
try (BufferedReader reader = Files.newBufferedReader(path)) {
process(reader.readLine());
}참고 링크
3 sources