JavaMedium#56
Java의 final 키워드를 변수·메서드·클래스에 적용했을 때 의미를 설명해주세요.
#Java#final#불변성#핵심개념
힌트
변경 불가, 오버라이드 불가, 상속 불가의 세 가지 의미입니다.
정답 및 해설
Java의 final 키워드를 변수·메서드·클래스에 적용했을 때 의미를 설명해주세요.
final 키워드는 Java에서 "변경 불가" 또는 "더 이상 확장 불가" 를 의미하는 수식어입니다. 변수, 메서드, 클래스에 각각 적용할 수 있으며, 적용 대상에 따라 의미가 달라집니다.
final 변수
한 번만 초기화 가능하며, 이후 재할당이 불가합니다.
원시 타입(primitive type) final 변수
값 자체가 고정됩니다.
final int MAX_SIZE = 100;
// MAX_SIZE = 200; // 컴파일 오류!
final double PI = 3.14159;
System.out.println(PI); // 3.14159
참조 타입(reference type) final 변수
참조(주소)가 고정됩니다. 객체 내부 상태는 여전히 변경 가능합니다.
final List<String> list = new ArrayList<>();
list.add("hello"); // OK — 객체 내부 상태 변경 가능
list.add("world"); // OK
// list = new ArrayList<>(); // 컴파일 오류! — 참조 재할당 불가
final StringBuilder sb = new StringBuilder("hello");
sb.append(" world"); // OK — 내부 상태 변경 가능
// sb = new StringBuilder(); // 컴파일 오류!
초기화 시점
final 변수는 선언 시점 또는 생성자에서 반드시 초기화해야 합니다.
public class Circle {
final double radius; // 선언 시 초기화하지 않음
public Circle(double radius) {
this.radius = radius; // 생성자에서 초기화 — OK
}
// final 필드는 생성자 이후 변경 불가
}
지역 변수와 blank final
public void process(boolean condition) {
final int value; // blank final — 선언만
if (condition) {
value = 10; // 조건 분기에서 한 번 초기화
} else {
value = 20;
}
System.out.println(value); // 반드시 초기화된 것이 보장됨
// value = 30; // 컴파일 오류!
}
final 메서드
자식 클래스에서 오버라이드할 수 없습니다. 부모 클래스의 특정 동작이 변경되지 않도록 보호할 때 사용합니다.
public class Template {
// 최종 알고리즘 — 오버라이드 불가
public final void execute() {
step1();
step2();
step3();
}
protected void step1() { System.out.println("기본 step1"); }
protected void step2() { System.out.println("기본 step2"); }
protected void step3() { System.out.println("기본 step3"); }
}
public class ConcreteTemplate extends Template {
@Override
protected void step1() { System.out.println("커스텀 step1"); } // OK
// @Override
// public void execute() { ... } // 컴파일 오류! final 메서드
}
final 클래스
상속 자체가 불가합니다. final 클래스는 확장할 수 없으며, 자식 클래스를 만들 수 없습니다.
대표적인 final 클래스
// Java 표준 라이브러리의 final 클래스들
final class String { ... } // 불변 문자열 보장
final class Integer { ... } // 불변 래퍼 클래스
final class Math { ... } // 유틸리티 클래스
// 상속 시도 — 컴파일 오류
// class MyString extends String { } // 불가!
사용자 정의 final 클래스
public final class ImmutablePoint {
private final int x;
private final int y;
public ImmutablePoint(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
// 새 객체를 반환 — 원본은 불변 유지
public ImmutablePoint translate(int dx, int dy) {
return new ImmutablePoint(x + dx, y + dy);
}
}
불변 객체(Immutable Object) 설계
final을 활용한 완전한 불변 객체를 만들 때는 다음 조건을 모두 충족해야 합니다.
public final class Money { // 1. 클래스를 final로 선언
private final Currency currency; // 2. 모든 필드를 private final
private final BigDecimal amount;
public Money(Currency currency, BigDecimal amount) {
this.currency = currency;
// 3. 가변 객체는 방어적 복사 (BigDecimal은 이미 불변이므로 생략)
this.amount = amount;
}
// 4. setter 없음
public Currency getCurrency() { return currency; }
public BigDecimal getAmount() { return amount; }
// 새 객체를 반환하는 방식으로 "변환" 구현
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new IllegalArgumentException("통화가 다릅니다.");
}
return new Money(currency, amount.add(other.amount));
}
}
활용 정리
| 적용 대상 | 의미 | 주요 활용 |
|---|---|---|
final 변수 | 재할당 불가 | 상수 정의, 불변 필드 |
final 메서드 | 오버라이드 불가 | 핵심 알고리즘 보호 |
final 클래스 | 상속 불가 | 불변 객체, 유틸리티 클래스 |
JVM 최적화
final 키워드를 사용하면 JVM이 인라이닝(inlining) 최적화를 적용할 수 있습니다.
final메서드와final클래스의 메서드는 동적 디스패치(virtual dispatch)가 불필요하므로 JIT 컴파일러가 직접 코드를 인라이닝할 수 있습니다.final변수는 컴파일 타임 상수(compile-time constant)로 처리되어 성능이 향상됩니다.
// 컴파일 타임 상수로 처리됨
static final int BUFFER_SIZE = 1024;
static final String PREFIX = "USER_";
// 컴파일 후 실제 값으로 인라이닝됨
byte[] buffer = new byte[1024]; // BUFFER_SIZE가 직접 치환됨
String key = "USER_" + userId; // PREFIX가 직접 치환됨