[Ninja Java] Q. Java의 final. finally, finallize의 차이는?
final, finally, finalize의 차이는 뭘까?
(틀린 게 있다면 편하게 댓글 달아주세요!)
✅ final
, finally
, finalize
차이 정리
1. final
정의: “변경할 수 없다”는 뜻. 변수, 메서드, 클래스에 붙일 수 있어.
- 변수에 사용: 초기화 후 값 변경 불가
final int x = 10;
x = 20; // 컴파일 에러
- 메서드에 사용: 오버라이딩 방지
public final void doSomething() { }
- 클래스에 사용: 상속 방지
public final class MyClass { }
2. finally
정의: try-catch-finally
블록에서 예외 발생 여부와 상관없이 반드시 실행되는 블록
- 주로 리소스 정리, 닫기 용도로 사용
try {
// 예외 발생 가능 코드
} catch (Exception e) {
// 예외 처리
} finally {
// 무조건 실행됨 (예외가 발생하든 말든)
}
3. finalize
- Object 클래스의 protected 메서드
- GC(Garbage Collector)가 객체를 메모리에서 제거하기 전에 호출되는 메서드
@Override
protected void finalize() throws Throwable {
try {
// 자원 정리 코드
} finally {
super.finalize();
}
}
🧠 하지만 지금은 거의 쓰지 않음!
- Java 9 이후로는 Deprecated(사용 권장하지 않음)
- 대신
try-with-resources
나 명시적인 자원 해제 사용 권장 (AutoCloseable
)
🔍 추가 질문
Q1. final
키워드를 매개변수에 붙이는 이유는 뭘까? 언제 써야 해?
Q2. try-finally
만 쓰고 catch
없이 쓰는 건 어떤 상황에서 유용할까?
Q3. finalize()
대신 요즘 자바에서 자원 정리를 권장하는 방식은 무엇일까? 예시 코드와 함께 설명해줘.
Q1
매개변수에 final을 붙이는 이유는 ‘변하면 안 되는 변수’ 인데 이게 무슨 소리냐면 메서드 안에서 그 매개변수 값을 바꾸지 못하도록 컴파일 시점에서 방지함
특히 익명 클래스나 람다 내부에서 외부 변수 참조시, final
혹은 사실상 final
인 변수만 참조할 수 있기 때문에 이런 상황에 유용함
public void foo(final int x) {
// x = 10; // 컴파일 에러!
}
Q2
try-finally
는 무조건 실행해야하 할 작업이 있지만 예외는 신경쓰지 않는 상황
예를 들어 리소스 닫기(close) 작업은 예외가 있든 없든 무조건 해야함 fianlly
블록에 둠 -> catch
없이 try-finally
를 쓰면 예외는 상위로 던지면서도, finally
에서 리소스 정리는 보장 가능
Q3
자바 9부터 finalize()
는 deprecated 되었고 불확실한 타이밍에 호출되고 성능 저하문제도 있어서 안쓴다고 함
요즘 자원 정리 권장 방식은?
try-with-resources
문법과 AutoCloseable
인터페이스를 사용
예시:
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
// br 사용
} catch (IOException e) {
e.printStackTrace();
}
// try가 끝나면 br.close() 자동 호출
try-with-resources
블록을 벗어나면 자동으로close()
호출AutoCloseable
을 구현한 객체만 사용 가능- 명시적으로 자원 해제를 보장하고,
finalize()
보다 훨씬 안정적이고 예측 가능
finalize()
와 try-with-resources
비교
finalize()
- 자바 객체가 가비지 컬렉터(GC)에 의해 메모리에서 삭제되기 전에 호출되는 메서드
- 보통 객체가 소유한 외부 자원(파일, 네트워크 등)을 해제하는 데 사용했음
- 단점: 호출 시점이 불확실하고, 호출 안 될 수도 있음. 성능 저하, 디버깅 어려움.
- Java 9부터 Deprecated → 더 이상 사용 권장하지 않음.
try-with-resources
- 자바 7부터 도입된 문법으로,
AutoCloseable
인터페이스를 구현한 객체의 자원을 자동으로 닫아주는 구조 try
블록이 끝나면 자동으로close()
호출- 장점: 호출 시점이 명확하고, 예외가 발생해도 안전하게 자원 해제 가능
- 코드가 간결해지고, 안전성 보장
AutoCloseable
인터페이스 직접 구현 예제
public class MyResource implements AutoCloseable {
public MyResource() {
System.out.println("Resource opened");
}
public void doSomething() {
System.out.println("Doing something with resource");
}
@Override
public void close() {
System.out.println("Resource closed");
}
}
public class Main {
public static void main(String[] args) {
try (MyResource r = new MyResource()) {
r.doSomething();
} // try 종료 시점에 자동으로 r.close() 호출
}
}
실행결과
Resource opened
Doing something with resource
Resource closed
MyResource
가 AutoCloseable
을 구현해서, try-with-resources
구문에서 자동으로 close()
를 호출함.
정리
구분 | finalize() | try-with-resources |
---|---|---|
호출 시점 | GC가 객체 제거 시 호출, 시점 불확실 | try 종료 시점에 명확하게 호출 |
사용 여부 | Java 9부터 deprecated | 권장되는 자원 관리 방법 |
안정성 | 불안정, 예외 처리 어려움 | 안정적, 예외 발생해도 자원 해제 보장 |
코드 복잡도 | 복잡하고 디버깅 어려움 | 간결하고 명확 |
1. try-with-resources
의 사용 시점과 상황
언제 사용하나?
- 명시적으로 닫아야 하는 자원(Resource)이 있을 때 쓴다.
- 대표적인 자원은 파일 입출력, 데이터베이스 연결, 네트워크 소켓, 스트림(Streams) 등이다.
왜 쓰나?
- 자원을 열었으면 반드시 닫아야 한다.
- 닫지 않으면 메모리 누수, 리소스 고갈, 데이터 손상 위험 발생
- 예외가 발생해도 자원을 안전하게 닫아주기 위해서다.
예시 상황
- 파일 읽기/쓰기할 때
- DB 커넥션, 쿼리 실행 시
- HTTP 클라이언트 소켓 다룰 때
2. finalize()
의 사용 시점과 상황
언제 쓰였나?
- 객체가 메모리에서 GC로 제거될 때 자원 해제를 위해 썼다.
-
근데 호출 시점이 불확실하고 성능 문제로 지금은 쓰지 않는다.
지금은?
- 거의 사용 안 함!
- 대신
try-with-resources
같은 명시적 자원 관리 권장 - 만약 꼭 필요하면,
java.lang.ref.Cleaner
같은 최신 대체 기술을 사용
3. Todo 앱 같은 백엔드에서의 적용
try-with-resources
적용 예
- 파일 업로드/다운로드 기능을 만들 때
- DB Connection, Statement, ResultSet 같은 리소스 관리
- 예를 들어, 파일에 로그를 남기거나, 임시 파일을 만들고 자동으로 닫거나 삭제할 때
💡 상황:
사용자가 완료한 Todo 리스트를 CSV 파일로 저장해서 다운로드하는 기능이 있다고 가정
✅ 목표:
- 완료된 Todo 항목을
List<Todo>
에서 가져옴 CSV
파일로 저장함try-with-resources
로FileWriter
를 안전하게 닫음
📦 예제 코드 (Spring Boot 환경 가정)
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
public class TodoExporter {
public void exportToCSV(List<Todo> todos, String filePath) {
try (FileWriter writer = new FileWriter(filePath)) {
writer.write("id,title,completed\n");
for (Todo todo : todos) {
writer.write(String.format("%d,%s,%b\n",
todo.getId(),
todo.getTitle(),
todo.isCompleted()));
}
System.out.println("CSV 파일이 저장되었습니다: " + filePath);
} catch (IOException e) {
System.err.println("파일 저장 중 오류 발생: " + e.getMessage());
}
// 여기서 writer는 자동으로 close됨!
}
}
🧾 Todo
클래스 예시
public class Todo {
private Long id;
private String title;
private boolean completed;
// 생성자, getter, setter 생략
}
🔍 설명
FileWriter
는 외부 리소스(파일 핸들)를 사용하는 클래스다.- 이걸 닫지 않으면 파일이 잠기거나, 데이터 유실, 리소스 누수가 생길 수 있음.
- 그래서
try-with-resources
로FileWriter
를 생성하면,try
블록이 끝나는 순간close()
가 자동 호출됨.
📌 실제 Todo 앱에선 어디에 쓸 수 있을까?
- 유저가 Todo 리스트를 다운로드할 때
- 작업 로그를 파일로 남길 때 (ex. 백업, 감사 로그 등)
- 스프링
JdbcTemplate
,BufferedReader
,ZipOutputStream
,InputStream
,OutputStream
같은 것들 다 해당됨
댓글남기기