final

한라봉봉·2023년 12월 16일

JAVA

목록 보기
11/16

final 변수

final 키워드

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

final - 지역 변수(매개변수 포함)

  1. final을 지역변수에 설정할 경우 최초 한번만 할당할 수 있다. 이후 변수 값 변경을 시도하면 컴파일 오류가 발생한다.
  2. final을 지역변수에 선언시 바로 초기화한 경우 이미 값이 할당되었기 때문에 값을 할당할 수 없다.
  3. 매개변수에 final이 붙으면 메서드 내부에서 매개변수의 값을 변경할 수 없다. 매서드 호출 시점에 사용된 값이 끝까지 사용된다.
    public static void main(String[] args) {
        //final 지역 변수1
        final int data1;
        data1 = 10; //최초 한번만 할당 가능
        //data1 = 20; //컴파일 오류

        //final 지역 변수2
        final int data2 = 10;
        //data2 = 20; //컴파일 오류
       
       
       method(10);
    }

    static void method(final int parameter) {
        //parameter = 20; //컴파일 오류, 호출시점 10으로 값 고정됨
    }

final-필드(멤버 변수)

  1. final을 필드에 사용할 경우 해당 필드는 생성자를 통해서 한번만 초기화 될 수있다.
public class ConstructInit {

    final int value;

    public ConstructInit(int value) {
        this.value = value;
    }
}
  1. final을 필드에서 사용후 바로 초기화할 수 있다.
public class FieldInit {

    final int value = 10;

}
  1. 동작 코드
    1) ConstructInit 과 같이 생성자를 사용해서 final 필드를 초기화 하는 경우, 각인스턴스 마다 다른 값을 할당 할 수 있다. 해당 필드는 생성자를 통해서 한번만 초기화 될 수있다.
    2) FieldInit과 같이 final 필드를 필드에서 초기화 하는 경우, 모든 인스턴스가 같은 값을 가진다. 여기서는 모두 10이 된다. 값 변경은 불가하다.
    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); //10
        System.out.println(constructInit2.value); //20

        //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);

static final

  1. FieldInit과 같이 final 필드를 필드에서 초기화 하는 경우 모든 인스턴스가 같은 값을 사용하기 때문에 메모리를 낭비하게 된다. 또한 중복이다. 이럴때 사용하면 좋은것이 static 영역이다.
public class FieldInit {

    static final int CONST_VALUE = 10;

}
  1. FieldInit.CONST_VALUE는 static 영역에 존재한다. 그리고 final 키워드를 사용해서 초기화 값이 변하지 않는다.
  2. JVM 상에서 CONST_VALUE(static 영역에 있으므로)는 단하나만 존재한다. 앞서 설명한 중복과 메모리 비효율 문제를 모두 해결할 수 있다.
  3. final+필드 초기화를 사용하는 경우 static을 붙여서 사용하는 것이 효과적이다.

상수(Constant)

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

자바 상수 특징

  1. static final 키워드 사용
  2. 대문자 사용. 구분은 _ (언더스코어)로 한다.(관례) : 일반적인 변수와 상수구분
  3. 필드를 직접 접근해서 사용
    1) 상수는 기능이 아니라 고정된 값 자체를 사용하는 것이 목적
    2) 상수는 값을 변경할 수 없다. 따라서 필드에 직접 접근해도 데이터가 변하는 문제가 발생하지 않는다.(그래서 노출하고 써도 됨)
//상수
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_MINUTES = 60;
    //애플리케이션 설정 상수
    public static final int MAX_USERS = 1000;
}
  1. 애플리케이션 안에는 다양한 상수가 존재할 수 있다. 수학, 시간 등등 실생활에서 사용하는 상수부터, 애플리케이션의 다양한 설정을 위한 상수들도 있따.

  2. 상수들은 애플리케이션 전반에서 사용되기 때문에 public을 자주 사용. 특정 위치에서만 사용한다면 다른 접근제어자를 사용해도 됨.

  3. 상수는 런타임에 변경 불가. 프로그램을 종료하고 상수 코드를 변경후 프로그램 다시실행 필요

상수는 중앙에서 값을 하나로 관리할 수 있다는 장점이있다.

아래 Constant 클래스에서 프로그램 최대 참여자 수는 상수로 관리된다. 따라서 최대 참여자 수를 바꾸더라도 사용하는 main쪽 코드 변경이 필요없다. 상수 초기화 값만 변경해주면 된다.(변경점 최소화.)
추가로, 사람이 인지할 수있는 변수명 MAX_USERS를 사용하여 최대 참여자수임을 알수있게 함으로써 가독성을 높인다.

아래 예시코드에서 만약 MAX_USERS 자리에 2000이 하드코딩으로 들어가있으면 최대 참여자수 변경시 일일히 바꿔야하며, 코드 작성자가 아니면 2000이 무슨 의미를 가진 숫자인지 바로 파악하기 어렵다.

public class Constant {
    //애플리케이션 설정 상수
    public static final int MAX_USERS = 2000;
}
    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 변수와 참조

  1. 참조형 변수 data에 final이 붙었다. 변수 선언시점에 참조값 할당으로 참조값 변경은 불가하다.
  2. 참조 대상의 객체값은 변경할 수 있다.
    data 내의 참조(주소)값은 다른 값으로 변경할 수 없다.
    그러나 data.value 는 final이 아니므로 값을 변경할 수 있다.
    public static void main(String[] args) {
        final Data data = new Data();
        //data = new Data(); final 변경 불가 컴파일 오류

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

정리

final은 매우 유용한 제약이다.
예를 들어서 고객의 id를 변경하면 큰 문제가 발생한다면 final로 선언하고 생성자로 값을 할당하자. 만약 어디에선가 실수로 id 값을 변경한다면 컴파일러가 문제를 찾아줄 것이다.

public class Member {
	private final String id; // final 키워드 사용
    private String name;
    
    public Member(String id, String name){
    	this.id = id;
        this.name = name;
    }
    
    public void changeData(String id, String name){
    	// this.id = id; // 컴파일 오류
        this.name = name;
    }
    
    public void print() {
    	System.out.println("id:" + id + "," + name);
    }
}
public static void main(String[] args){
	Member member = new Member("myId", "kim");
    member.print();
    // member.changeData("myId2", "seo"); // 컴파일 오류
    member.changeData("seo");
    
}
profile
백엔드 개발공부 로그를 기록합니다

0개의 댓글