'김영한의 실전 자바 - 기본편' 강의를 들으면서 복습할만한 내용을 정리하였다.

9. final

9.1 final 변수와 상수

final 키워드는 이름 그대로 끝! 이라는 뜻이다.

변수에 final 키워드가 붙으면 더는 값을 변경할 수 없다.

  • final 을 지역 변수에 설정할 경우 최초 한번만 할당할 수 있다. 이후에 값을 변경하려면 컴파일 오류가 발생한다.

  • final 을 지역 변수 선언시 바로 초기화 한 경우 이미 값이 할당되었기 때문에 값을 할당할 수 없다.

  • 매개변수에 final 이 붙으면 메서드 내부에서 매개변수의 값을 변경할 수 없다. 따라서 메서드 호출 시점에 사용된 값이 끝까지 사용된다.

final - 필드(멤버 변수)

 // final 필드 - 필드 초기화
public class FieldInit {
	static final int CONST_VALUE = 10;
    final int value = 10;
}
  • static 변수에도 final 을 선언할 수 있다.
    • 이러한 경우를 상수라고 한다.

  • FieldInit 과 같이 final 필드를 초기화 하는 경우, 모든 인스턴스가 다음 오른쪽 그림과 같이 같은 값을 가진다.
  • 여기서는 FieldInit 인스턴스의 모든 value 값은 10 이 된다.
  • 모든 인스턴스가 같은 값을 사용하기 때문에 결과적으로 메모리를 낭비하게 된다. 또 메모리 낭비를 떠나서 같은 값이 계속 생성되는 것은 개발자가 보기에 명확한 중복이다. 이럴 때 사용하면 좋은 것이 static 영역이다.

static final

  • FieldInit.MY_VALUEstatic 영역에 존재한다. 그리고 final 키워드를 사용해서 초기화 값이 변하지 않는다.

  • static 영역은 단 하나만 존재하는 영역이다. MY_VALUE 변수는 JVM 상에서 하나만 존재하므로 앞서 설명한 중복과 메모리 비효율 문제를 모두 해결할 수 있다.

이런 이유로 필드에 final + 필드 초기화를 사용하는 경우 static 을 붙여서 사용하는 것이 효율적이다.

상수(Constant)

상수는 변하지 않고, 항상 일정한 값을 갖는 수를 말한다. 자바에서는 보통 단 하나만 존재하는 변하지 않는 고정된 값을 상수라 한다.

이런 이유로 상수는 static final 키워들 사용한다.

자바 상수 특징

  • static final 키워드를 사용한다.
  • 대문자를 사용하고 구분은 _(언더스코어)로 한다.
  • 필드를 직접 접근해서 사용한다.
    • 상수는 기능이 아니라 고정된 값 자체를 사용하는 것이 목적이다.
    • 상수는 값을 변경할 수 없다. 따라서 필드에 직접 접근해도 데이터가 변하는 문제가 발생하지 않는다.
  • 보통 이런 상수들은 애플리케이션 전반에서 사용되기 떄문에 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명에서 2000명으로 변경해야 하면 2곳의 변경 포인트가 발생한다. 만약 애플리케이션의 100곳에서 이 숫자를 사용했다면 100곳 모두 변경해야 한다.

  • 매직 넘버 문제가 발생했다. 숫자 1000 이라는 것이 무슨 뜻일까? 이 값만 보고 이해하기 어렵다.

public class ConstantMain2 {
 	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("게임에 참가합니다.");
        }
    }
}
  • Constant.MAX_USERS 상수를 사용했다. 만약 프로그램 최대 참여자 수를 변경해야 하면 Constant.MAX_USERS 의 상수 값만 변경하면 된다.
  • 매직 넘버 문제를 해결했다. 숫자 1000 이 아니라 사람이 인지할 수 있게 MAX_USERS 라는 변수명으로 코드를 이해할 수 있다.

9.2 final 변수와 참조

변수는 크게 기본형 변수와 참조형 변수가 있다.

public class Data {
	public int value;
}
public class FinalRefMain {

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

참조형 변수 datafinal 이 붙었다. 변수 선언 시점에 참조값을 할당했으므로 더는 참조값을 변경할 수 없다.

그런데 참조 대상의 객체의 멤버 변수인 value 는 값을 변경할 수 있다.

  • 참조형 변수 datafinal 이 붙었다. 이 경우 참조형 변수에 들어있는 참조값을 다른 값으로 변경하지 못한다. 쉽게 이야기해서 이제 다른 객체를 참조할 수 없다. 그런데 이것의 정확한 뜻을 잘 이해해야 한다. 참조형 변수에 들어있는 참조값만 변경하지 못한다는 뜻이다. 이 변수 이외에 다른 곳에 영향을 주는 것이 아니다.
  • Data.vlauefinal 이 아니다. 따라서 값을 변경할 수 있다.

final 은 매우 유용한 제약이다. 만약 특정 변수의 값을 할당한 이후에 변경하지 않아야 한다면 final을 사용하자.


profile
가오리의 개발 이야기

0개의 댓글