Java

[인프런] final 변수와 상수

sejin2 2024. 5. 27. 12:15

final 키워드는 이름 그대로 끝이라는 뜻이다. 변수에 final 키워드가 붙으면 더는 값을 변경할 수 없다. 
final은 class, method를 포함한 여러 곳에 붙을 수 있다.

지역변수에  final 할당

public class FinalLocalMain {
    public static void main(String[] args) {
        // final 지역 변수
        final int data1;
        data1 = 10; // 최초 한번만 할당 가능
        // data1=20; // 컴파일 오류
    }
}

final을 지역변수에 설정할 경우 최초 한번만 할당할 수 있다. 이후에 변수의 값을 변경하려면 컴파일 오류가 발생한다.
final을 지역변수 선언 시 바로 초기화 한 경우 이미 값이 할당되었기 때문에 값을 할당할 수 없다. 
매개변수에 final이 붙으면 메서드 내부에서 매개변수의 값을 변경할 수 없다. 따라서 메서드 호출 시점에 사용된 값이 끝까지 사용된다.

 final - 필드 ( 멤버 변수 )

public class ConstructInit {
    final int value;

    public ConstructInit(int value) {
        this.value = value;
    }
}

아직 초기값을 정하지 않았기 때문에 생성자로 초기값을 설정해줄 수 있다.

public class FiledInit {
	static final int CONST_VALUE = 10;
    final int value = 10;
}

여기는 이미 초기값이 설정되어있기 때문에 생성자로 초기값을 설정할 수 없다. 

public class FinalFieldMain {
    public static void main(String[] args) {
        // final 필드 - 생성자 초기화
        System.out.println("생성자 초기화");
        ConstructInit constructInit1 = new ConstructInit(10);
        ConstructInit constructInit2 = new ConstructInit(20);
        System.out.println(constructInit1.value);
        System.out.println(constructInit2.value);

        // final 필드 - 필드 초기화
        System.out.println("필드 초기화");
        FieldInit fieldInit1 = new FieldInit();
        FieldInit fieldInit2 = new FieldInit();
        FieldInit fieldInit3 = new FieldInit();
        System.out.println(fieldInit1.value);
        System.out.println(fieldInit2.value);
        System.out.println(fieldInit3.value);

        // 상수
        System.out.println("상수");
        System.out.println(FieldInit.CONST_VALUE);
    }
}

ConstructInit과 같이 생성자를 사용해서 final 필드를 초기화 하는 경우, 각 인스턴스마다 final 필드에 다른 값을 할당할 수 있다. final을 사용했기 때문에 생성 이후에 변경은 불가능하다.
FieldInit과 같이 final 필드를 필드에서 초기화 하는 경우, 모든 인스턴스가 다음 오른쪽 그림과 같이 같은 값을 가진다.
생성자 초기화와는 다르게 필드 초기화는 필드 코드에 해당 값이 미리 정해져있기 때문이다. 모든 인스턴스가 같은 값을 사용하기 때문에 결과적으로 메모리를 낭비하게 되고, 메모리 낭비를 떠나 같은 값이 계속 생성되는 것은 개발자가 보기에 명확한 중복이다. => 이럴 때 사용하면 좋은 것이  static 영역이다. 

static final을 사용하면 static 영역에 존재하고 단 하나만 존재하며 초기화 값이 변하지 않는다.

상수 ( Constant )

상수는 변하지 않고, 항상 일정한 값을 갖는 수를 말한다. 자바에서는 보통 단 하나만 존재하는 변하지 않는 고정된 값을 상수라고 한다. 이런 이유로 상수는 static final 키워드를 사용한다. 

자바상수의 특징

  • static final 키워드를 사용한다.
  • 대문자를 사용하고 구분은 _ 로 한다. ( 관례 )
    • 일반적인 변수와 상수를 구분하기 위해 이렇게 한다.
  • 필드를 직접 접근해서 사용한다.
    • 상수는 기능이 아니라 고정된 값 자체를 사용하는 것이 목적이다.
    • 상수는 값을 변경할 수 없으므로 필드에 직접 접근해도 데이터가 변하는 문제가 발생하지 않는다.
// 상수
public class Constant {
    // 수학 상수
    public static final double PI = 3.14;
    // 시간 상수
    public static final int HOURS_IN_DAY = 24;
    public static final int MINUTES_IN_HOUR = 60;
    public static final int SECONDS_IN_MINUTE = 60;
    //애플리케이션 설정 상수
    public static final int MAX_USERS = 1000;
}

애플리케이션에는 다양한 상수가 존재할 수 있다. 보통 이런 상수들은 애플리케이션 전반에서 사용되기 때문에 public을 자주 사용한다. ( 특정 위치에서만 사용된다면 다른 접근 제어자를 사용하면 된다 )
상수는 중앙에서 값을 하나로 관리할 수 있다는 장점이 있다.
상수는 런타임에 변경할 수 없다. 상수를 변경하려면 프로그램을 종료하고, 코드를 변경한 다음에 프로그램을 다시 실행해야 한다.

public class ConstantMain1 {
    public static void main(String[] args) {
        System.out.println("프로그램 최대 참여자 수 " + 1000);
        int currentUserCount = 999;
        process(currentUserCount++);
        process(currentUserCount++);
        process(currentUserCount++);
    }

    private  static void process(int currentUserCount) {
        System.out.println("참여자 수 : " + currentUserCount);
        if(currentUserCount > 1000) {
            System.out.println("대기자로 등록합니다.");
        }else {
            System.out.println("게임에 참여합니다.");
        }
    }
}

현재 코드에서 최대 참여자수를 변경하기 위해서는 1000이라는 수를 각각 바꿔주어야 하고, 
프로그램 최대 참여자 수 라고 명시되어 있지 않으면 1000이라는 숫자가 무엇을 의미하는지 알 수 없다. 
따라서 이때 상수를 사용해주면 된다. 

public class ConstantMain1 {
    public static void main(String[] args) {
        System.out.println("프로그램 최대 참여자 수 " + Constant.MAX_USERS);
        int currentUserCount = 999;
        process(currentUserCount++);
        process(currentUserCount++);
        process(currentUserCount++);
    }

    private  static void process(int currentUserCount) {
        System.out.println("참여자 수 : " + currentUserCount);
        if(currentUserCount > Constant.MAX_USERS) {
            System.out.println("대기자로 등록합니다.");
        }else {
            System.out.println("게임에 참여합니다.");
        }
    }
}

그러면 한 곳에서 상수를 관리할 수 있게 된다. 그리고 변수명만 보고도 무엇을 의미하는지 정확히 알 수 있다. 

final 변수와 참조 

final은 변수의 값을 변경하지 못하게 막는다. 

  • 변수는 크게 기본형 변수와 참조형 변수가 있다. 
  • 기본형 변수는 10, 20 같은 값을 보관하고, 참조형 변수는 객체의 참조값을 보관한다.
    • final을 기본형 변수에 사용하면, 값을 변경할 수 없다.
    • final을 참조형 변수에 사용하면 참조값은 변경할 수 있다. 
public class Data {
    public int value;
}
public class FinalRefMain {
    public static void main(String[] args) {
        final Data data = new Data(); // 참조형

        // 참조 대상의 값은 변경 가능
        data.value = 10;
        System.out.println(data.value);
        data.value = 20;
        System.out.println(data.value);

    }
}

참조형 변수 data에 final이 붙으면 변수 선언 시점에 참조값을 할당했으므로 참조값을 변경할 수 없다.
그런데 참조 대상의 객체의 값은 변경할 수 있다. 
참조형 변수 data에 final이 붙으면 다른 객체를 참조할 수 없게 된다. 참조형 변수에 들어있는 참조값을 변경하지 못한다는 의미이다. Data.value는 final이 아니기 때문에 값 변경이 가능하다. 
즉, 참조형 변수에 final이 붙으면 참조 대상 자체를 다른 대상으로 변경하지 못한다는 것이지, 참조하는 대상의 값은 변경할 수 있다. 

 

 

 

공부 내용: [인프런] 김영한의 실전 자바 - 기본편

'Java' 카테고리의 다른 글

[인프런] 상속과 메서드 오버라이딩  (0) 2024.05.27
[인프런] 상속  (0) 2024.05.27
[인프런] static 메서드  (0) 2024.05.26
[인프런] static 변수  (0) 2024.05.26
[인프런] 스택과 큐 자료 구조  (0) 2024.05.25