상수(Constant)의 개념
상수는 프로그램에서 한 번 초기화된 후 변경되지 않는 변수를 의미하며, Java에서는 final static으로 선언한다.
상수 사용 이유
- 가독성과 유지 보수성
상수에 의미 있는 이름을 부여함으로써 코드를 더 쉽게 읽을 수 있게 하고, 프로그램 내에서 값이 변경될 경우 상수만 수정하면 되므로 유지 보수가 용이해진다.
- 안정성
상수의 값은 프로그램 실행 중 변경되지 않기 때문에 실수로 값을 수정하는 것을 방지할 수 있다.
- 재사용 가능성
상수는 여러 프로그램에서 공통으로 사용될 수 있어 중복 코드를 줄이고 코드의 재사용성을 높인다.
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의 이점
- 메모리 절약
각 객체마다 변수를 따로 저장할 필요가 없어 메모리 사용을 절약할 수 있다.
- 전역 접근성
클래스 이름을 통해 직접 접근할 수 있어, 인스턴스를 생성하지 않고도 정적 변수와 메서드에 접근할 수 있다.
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의 이점
- 안정성
final 키워드를 사용함으로써 변수, 메서드, 클래스의 변경을 방지할 수 있어, 프로그램의 안정성을 높인다. 이는 코드가 의도한 대로 작동하도록 보장한다.
- 명확한 의도 표현
final을 사용하면 코드의 의도를 명확하게 표현할 수 있다. 예를 들어, 상수를 정의할 때 final을 사용하면 이 값이 변경되지 않을 것임을 다른 개발자에게 분명히 전달할 수 있다.
- 불변 객체 생성
final 변수를 사용하여 불변 객체를 생성할 수 있다. 이는 객체의 상태가 생성 후 변경되지 않음을 의미하며, 멀티스레딩 환경에서 안전하게 사용할 수 있다.
static final 조합
static final 조합은 Java에서 상수를 정의할 때 자주 사용되는 패턴으로, 모든 인스턴스가 공유하는 불변의 값을 생성할 수 있다.
즉, static으로 선언된 변수는 클래스 레벨에서 공유되며, final로 선언된 변수는 한 번 초기화된 이후에는 그 값을 변경할 수 없다.
특징
- 공유성
static으로 선언된 변수는 클래스의 모든 인스턴스가 동일한 값을 공유하므로, 메모리 사용을 최적화할 수 있다.
- 불변성
final로 선언된 변수는 초기화 이후에 값을 변경할 수 없으므로, 프로그램의 안정성을 높인다.
- 전역 접근성
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 키워드를 사용하여 정의하며, 두 가지 방식으로 선언할 수 있다.
- 상수만 정의하는 열거형:
public enum Day {
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
}
- 값이 있는 열거형:
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; } |
| 타입 안전성 | 부족 (잘못된 값 사용 가능) | 높음 (정의된 값만 사용 가능) |
| 가독성 | 여러 상수 관리 시 저하될 수 있음 | 관련된 상수를 그룹화하여 향상됨 |
| 유지 보수 | 여러 곳 수정 필요 | 정의만 수정하면 됨 |
| 기능 | 단순 값 정의 | 메서드 추가 가능 |
| 유연성 | 다양한 데이터 타입 지원 | 미리 정의된 값만 사용 가능 |