본문 바로가기

Java

[JAVA]final 키워드와 상수(static final)의 이해

final 필드

final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 도중에 수정할 수 없게 되는 필드이다.

선언법

final 타입 필드 = 초기값;
final int num = 123;

final 필드에 초기값을 주는 방법은 두 가지이다.

  • 필드 선언 시에 초기값을 지정한다.
    • 단순히 값을 선언할 때 사용하면 편리하다.
  • 생성자에서 초기값을 준다.
    • 외부에서 데이터를 초기화해야 되는 상황에 사용한다.
    • 복잡한 초기화 코드가 필요한 경우에 사용할 수 있다.
    • 만약 생성자에서도 final 필드가 초기화되지 않는다면 컴파일 에러가 발생한다.

예제

Person.java

public class Person {
    //final 필드 두개와 일반 필드 한개 선언
    final int age = 21; //선언 즉시 초기화
    final String university;//선언 시 초기화 안함.
    String name;

    public Person(String university, String name) {//대학교와 이름을 생성자에서 초기화.
        this.university = university;
        this.name = name;
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("한국 대학교", "홍길동");

        System.out.println(p1.age);
        System.out.println(p1.university);
        System.out.println(p1.name);

        // age와 university 필드는 final 필드이므로 수정을 하려 하면 에러가 발생한다.
        //p1.age = 22; 
        //p1.university = "다른대학교";
        p1.name = "김철수";
    }
}

출력 결과

21
한국 대학교
홍길동

final 클래스, 메소드

클래스와 메소드에 final 키워드가 붙게되면 상속과 관련이 생긴다.

  • final 클래스
    • 클래스를 선언할 때 앞에 final 키워드를 붙이게 되면 클래스가 최종 클래스가 된다.
    • 따라서 이 클래스는 상속할 수 없는 클래스가 된다. 부모 클래스가 될 수 없다는 것이다.
    • 대표적인 예로 String 클래스가 final 클래스이다.
public final class String { ... } (o)

public final class Child extends String { ... } (x) //String 클래스는 final 클래스이므로 상속을 할 수 없다.
  • final 메소드
    • 메소드를 선언할 때 앞에 final 키워드를 붙이게 되면 메소드가 최종 메소드가 된다.
    • 따라서 이 메소드는 오버라이딩 할 수 없다. 자식 클래스에서 부모 클래스의 메소드를 오버라이딩 하려 할 때 이 메소드가 부모 클래스에서 final 메소드로 되어 있다면 오버라이딩 할 수 없다는 것이다.

상수(static final)

상수는 불변의 값을 의미한다. 수학에서 쓰이는 파이 값이 대표적인 예이다. 자바에서는 불변의 값을 저장하는 필드를 상수(constant)라고 한다. final 필드는 한 번 초기화하면 수정할 수 없는 필드이다. 그렇다면 final필드를 상수라고 해도 되지 않나 의문이 든다. 하지만 final 필드와 상수를 동일시 취급하지는 않는다. final 필드는 객체마다 저장된다. 이에 반해 불변의 값 상수는 공용성을 띤다. 객체마다 저장할 필요가 없는 것이다. 생성자를 통해서 초기화를 받아 여러 가지 값을 가질 수 있다. 하지만 상수는 그렇지 않다. 파이는 어느 상황에서도 3.14...이지 않은가? 때문에 상수와 final 필드는 구분해야 한다. 상수는 final 필드에 공용성을 부여하면 된다. 바로 static이 필요하다. 따라서 상수는 static final 필드라고 표현 가능하다.

static final 타입 상수 = 초기값;

정적 블록을 사용해서 초기화하는 것도 가능하다.

static final 타입 상수;
static {
  상수 = 초기값;
}

상수의 이름을 지정할 때는 전부 영어 대문자로 하는 것이 일반적이다.

static final double PI = 3.14;