상수와 열거형: static, final, static final, enum의 이해

상수(Constant)의 개념

상수는 프로그램에서 한 번 초기화된 후 변경되지 않는 변수를 의미하며, Java에서는 final static으로 선언한다.

상수 사용 이유

  1. 가독성과 유지 보수성

상수에 의미 있는 이름을 부여함으로써 코드를 더 쉽게 읽을 수 있게 하고, 프로그램 내에서 값이 변경될 경우 상수만 수정하면 되므로 유지 보수가 용이해진다.

  1. 안정성

상수의 값은 프로그램 실행 중 변경되지 않기 때문에 실수로 값을 수정하는 것을 방지할 수 있다.

  1. 재사용 가능성

상수는 여러 프로그램에서 공통으로 사용될 수 있어 중복 코드를 줄이고 코드의 재사용성을 높인다.


static 키워드

static은 클래스 레벨에서 정의된 변수로, 모든 인스턴스가 공유하며 클래스에 직접 접근할 수 있는 속성을 의미한다.

예시 코드

public class Counter {
    private static int count = 0; // 공유되는 정적 변수
    private final String name; // 인스턴스마다 고유한 이름

    public Counter(String name) {
        this.name = name; // 생성자를 통해 이름 초기화
    }

    public static int getCount() {
        return count; // 정적 변수에 접근하는 메서드
    }

    public String getName() {
        return name; // 인스턴스 변수에 접근하는 메서드
    }
}

위 코드는 count라는 숫자와 name이라는 사람의 이름을 가지는 Counter 클래스를 정의하고 있다. 이 클래스를 참조할 때 static비정적(Non-static) 변수의 차이를 살펴보겠다.

public class Main {
    public static void main(String[] args) {
        // 정적 메서드 호출
        System.out.println("Total Count: " + Counter.getCount());

        // 비정적 메서드 호출
        Counter counterInstance = new Counter("grayson"); // 인스턴스 생성
        System.out.println("USERNAME: " + counterInstance.getName());
    }
}

위의 예제에서 static으로 선언된 변수는 인스턴스를 생성하지 않고도 공유된 공간에 저장되어 참조할 수 있는 반면, 비정적(Non-static) 변수는 인스턴스 객체를 선언해야만 해당 메서드를 호출할 수 있다.

static의 이점

  1. 메모리 절약

각 객체마다 변수를 따로 저장할 필요가 없어 메모리 사용을 절약할 수 있다.

  1. 전역 접근성

클래스 이름을 통해 직접 접근할 수 있어, 인스턴스를 생성하지 않고도 정적 변수와 메서드에 접근할 수 있다.


final 키워드

final은 Java에서 변수, 메서드, 클래스에 적용할 수 있는 키워드로, 한 번 초기화된 이후에는 그 값을 변경할 수 없음을 의미한다.

final 변수

final로 선언된 변수는 한 번 초기화된 이후 그 값을 변경할 수 없다. 변수를 정의할 때 반드시 값을 할당해야 하며, 이후에는 재할당이 불가능하다.

public class Counter {
    private final String name; // final 변수 선언

    public Counter(String name) {
        this.name = name; // 생성자에서 값 초기화
    }

    public void setName(String newName) {
        // ERROR: java: Cannot assign a value to final variable 'name'
        this.name = newName; // 재할당 시도 시 오류 발생
    }
}

위 코드에서 name 변수는 final로 선언되었기 때문에, 생성자에서 초기화한 이후에는 값을 변경할 수 없으며, 재할당을 시도할 경우 “Cannot assign a value to final variable ‘name'” 오류가 발생한다.

final 메서드

final 메서드는 서브클래스에서 오버라이드할 수 없는 메서드로, 특정 기능을 변경하지 못하도록 보장하여 일관된 동작을 유지한다.

final 클래스

final 클래스는 다른 클래스가 상속할 수 없는 클래스이며, 이를 통해 클래스의 구조와 기능을 변경할 수 없게 한다.

final의 이점

  1. 안정성

final 키워드를 사용함으로써 변수, 메서드, 클래스의 변경을 방지할 수 있어, 프로그램의 안정성을 높인다. 이는 코드가 의도한 대로 작동하도록 보장한다.

  1. 명확한 의도 표현

final을 사용하면 코드의 의도를 명확하게 표현할 수 있다. 예를 들어, 상수를 정의할 때 final을 사용하면 이 값이 변경되지 않을 것임을 다른 개발자에게 분명히 전달할 수 있다.

  1. 불변 객체 생성

final 변수를 사용하여 불변 객체를 생성할 수 있다. 이는 객체의 상태가 생성 후 변경되지 않음을 의미하며, 멀티스레딩 환경에서 안전하게 사용할 수 있다.


static final 조합

static final 조합은 Java에서 상수를 정의할 때 자주 사용되는 패턴으로, 모든 인스턴스가 공유하는 불변의 값을 생성할 수 있다.

즉, static으로 선언된 변수는 클래스 레벨에서 공유되며, final로 선언된 변수는 한 번 초기화된 이후에는 그 값을 변경할 수 없다.

특징

  1. 공유성

static으로 선언된 변수는 클래스의 모든 인스턴스가 동일한 값을 공유하므로, 메모리 사용을 최적화할 수 있다.

  1. 불변성

final로 선언된 변수는 초기화 이후에 값을 변경할 수 없으므로, 프로그램의 안정성을 높인다.

  1. 전역 접근성

static final 변수를 사용하면 클래스 이름을 통해 직접 접근할 수 있어, 인스턴스를 생성하지 않고도 이 값에 접근할 수 있다.

활용 방법

static final 조합은 주로 상수 값을 정의할 때 사용된다. 예를 들어 수학적 상수, 설정 값, 에러 코드 등을 정의할 때 유용하다.

public class MathConstants {
    public static final double PI = 3.14159; // 원주율 상수
    public static final int MAX_USERS = 100; // 최대 사용자 수 상수
}

열거형(Enum)

열거형(enum)은 Java에서 미리 정의된 상수의 집합을 나타내는 데이터 타입이다.

기본 선언 방법

열거형은 enum 키워드를 사용하여 정의하며, 두 가지 방식으로 선언할 수 있다.

  1. 상수만 정의하는 열거형:
public enum Day {
        SUNDAY,
        MONDAY,
        TUESDAY,
        WEDNESDAY,
        THURSDAY,
        FRIDAY,
        SATURDAY
    }
  1. 값이 있는 열거형:
public enum Rank {
        FIRST("Winner", 1),
        SECOND("Runner Up", 2),
        THIRD("Participant", 3);
    
        private String title;
        private int position;
    
        private Rank(String title, int position) {
            this.title = title;
            this.position = position;
        }
    
        public String getTitle() {
            return title;
        }
    
        public int getPosition() {
            return position;
        }
    }

예시 코드

public enum Rank {
    FIRST(6, 2_000_000_000),
    SECOND(5, 30_000_000),
    THIRD(5, 1_500_000),
    FOURTH(4, 50_000),
    FIFTH(3, 5_000),
    MISS(0, 0);

    private int countOfMatch; // 매칭된 숫자의 개수
    private int winningMoney;  // 상금

    private Rank(int countOfMatch, int winningMoney) {
        this.countOfMatch = countOfMatch; // 생성자에서 매칭 개수 초기화
        this.winningMoney = winningMoney; // 생성자에서 상금 초기화
    }

    public int getCountOfMatch() {
        return countOfMatch; // 매칭 개수를 반환하는 메서드
    }

    public int getWinningMoney() {
        return winningMoney; // 상금을 반환하는 메서드
    }
}

사용 방법

열거형을 사용하면 특정 상수에 대한 속성에 쉽게 접근할 수 있다.

public class Main {
    public static void main(String[] args) {
        System.out.println(Rank.FIFTH.getCountOfMatch()); // FIFTH 등급의 매칭 개수를 출력
        System.out.println(Rank.FIRST.name()); // FIRST 상수의 이름을 출력
        System.out.println(Rank.valueOf("FIRST")); // FIRST 상수를 이름으로 다시 가져옴
        System.out.println(Rank.FIFTH.getWinningMoney()); // FIFTH 등급의 상금을 출력
    }
}

출력:

3
FIRST
FIRST
5000

enum의 이점

  • 허용 가능한 값을 제한: 특정 값만 사용하도록 제한할 수 있어 코드의 안정성을 높인다.
  • 수정 시 변경 범위 최소화: 열거형만 수정하면 되므로 유지 보수가 용이하다.
  • 컴파일 시 타입 및 유효성 체크: 잘못된 값을 사용했을 때 컴파일러가 오류를 알려준다.
  • 가독성 향상: 관련 상수를 그룹화하여 코드가 간단해지고 이해하기 쉬워진다.
  • 안전성 보장: 인스턴스의 생성과 상속을 방지하여 상수값의 안전성을 보장한다.

상수와 열거형의 비교

구분 상수 (Constant) 열거형 (Enum)
정의 final 키워드를 사용하여 한 번 초기화된 후 변경할 수 없는 변수 미리 정의된 상수의 집합을 나타내는 데이터 타입
선언 방법 public static final int MAX_USERS = 100; public enum Direction { NORTH, SOUTH, EAST, WEST; }
타입 안전성 부족 (잘못된 값 사용 가능) 높음 (정의된 값만 사용 가능)
가독성 여러 상수 관리 시 저하될 수 있음 관련된 상수를 그룹화하여 향상됨
유지 보수 여러 곳 수정 필요 정의만 수정하면 됨
기능 단순 값 정의 메서드 추가 가능
유연성 다양한 데이터 타입 지원 미리 정의된 값만 사용 가능
위로 스크롤