전체 목록
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가 직접 치환됨