먼저 final을 이용해서 불변 객체를 만들기 전에 final의 개념과 Java에서 불변 객체가 어떤 의미인지 알아보자
final은 자바에서 특정 값이나 상태를 변경하지 않도록 보장하는 키워드인데, 사용 위치에 따라 3가지로 분류할 수 있다.
1. 변수에 사용
변수에 final을 붙이면 변수의 값을 한 번만 할당하도록 보장하는 역할을 한다.
1-1. 기본 데이터 타입에 final 사용
기본 데이터 타입(int, double, char 등)에 final을 붙이면, 값이 한 번 할당된 후에는 변경할 수 없다.
public class Example {
public static void main(String[] args) {
final int number = 10;
number = 20; // 컴파일 오류 발생. 'number'는 재할당 불가능
}
}
1-2. 참조형 데이터 타입에 final 사용
참조형 데이터 타입(List, Map, Object 등)에 final을 붙이는 경우에는 변수가 가리키는 참조 자체가 변경될 수 없음을 의미한다. 즉, 참조 변수에 다른 객체를 할당하는 것은 금지되지만, 참조가 가리키는 객체 내부의 속성이나 값은 변경이 가능하다.
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String[] args) {
final List<String> names = new ArrayList<>();
names.add("Alice"); // 리스트에 요소 추가 : 가능
names.add("Bob"); // 리스트에 요소 추가 : 가능
names = new ArrayList<>(); // 컴파일 오류 발생 : 참조 변경 불가능
}
}
1-3. final을 사용하는 사례 예시
public class Constants {
public static final int NUMBER = 10;
public static final String USER_NAME = "John";
}
주의) final로 지정된 변수는 반드시 초기화되어야 하며, 초기화 시점은 선언 시점 또는 생성자 내에서 가능하다.
2. 메서드에 사용
메서드에 final을 사용하는 것은 서브클래스에서 오버라이드하지 못하도록 막는 것이 목적이다. 특정 메서드가 서브클래스에서도 동일하게 동작해야 하는 경우, final을 붙여 동작을 변경하지 않도록 보장하는 것이다.
public class Account {
private double balance;
public Account(double initialBalance) {
this.balance = initialBalance;
}
// 계좌 잔액 조회 메서드를 final로 선언하여 변경되지 않도록 보호
public final double getBalance() {
return balance;
}
// 입금 메서드는 오버라이드가 가능하도록 설정
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
}
2-1. final 메서드 사용 시 주의사항
과도하게 final을 사용해 메서드의 모든 동작을 불변으로 만들면 서브클래스의 유연성이 떨어져, 필요한 상황에서 메서드를 재정의하지 못해 불편할 수 있으니 필요한 메서드에만 사용하자.
final 메서드는 서브클래스의 확장도 막지만, 상위 클래스의 동작에 대한 의존성을 지나치게 높일 수 있으니 신중히 고려할 것!
3. 클래스에 사용
클래스에 final을 붙이면 해당 클래스를 상속할 수 없도록 금지한다. 즉, final 클래스를 상속하려고 하면 컴파일 오류가 발생한다.
public final class Constants {
public static final int NUMBER = 10;
public static final String DEFAULT_USER = "Guest";
}
위 예제의 Constants 클래스는 상수를 정의하는 용도로만 사용되며, 상속이 필요하지 않다.
자바에서의 불변 객체는?
자바에서 불변 객체는 프로그램의 안전성과 예측 가능성을 높이는 데 중요한 역할을 한다. 불변 객체는 생성된 이후 상태가 변경되지 않기 때문에 여러 가지 면에서 이점을 가지고, 특히! 멀티스레드 환경과 보안성 측면에서 큰 의미를 가진다.
1. 동시성 안정성 (Thread SafeTy)
- 불변 객체는 상태가 변경되지 않기 때문에 여러 스레드에서 안전하게 공유할 수 있다. 일반적인 가변 객체는 여러 스레드가 동시에 접근하여 수정하려고 하면 데이터의 불일치가 발생할 수 있지만, 불변 객체는 이런 위험이 전혀 없다.
- 멀티스레드 환경에서 불변 객체는 동기화 작업이 필요 없으므로 성능과 코드 간결성 면에서 큰 장점!
2. 보안 강화
- 불변 객체는 상태가 변경되지 않으므로 중요한 데이터가 외부에서 변조될 위험이 줄어든다.
3. 예측 가능성 및 디버깅 용이성
- 불변 객체는 한 번 초기화된 이후 상태가 변하지 않기 때문에 예측 가능한 동작을 한다.
- 디버깅 과정에서 객체의 상태가 어디서 변경되었는지 추적할 필요가 없다.
4. 불변 객체의 신뢰성
- 설계 의도를 명확히 전달한다. 생성 후 변경할 필요가 없음을 표현!!
5. 캐시와 성능 최적화 용이성
- 불변 객체는 상태가 변하지 않기 때문에 반복 계산이 불필요하다. 이를 통해 캐싱이나 메모이제이션을 적용할 수 있다.
- String 객체는 자바에서 대표적인 불변 객체인데, 같은 문자열이 여러 번 사용될 때 메모리를 절약하기 위해 Spring Pool에 캐시되어 재사용 된다.
6. 자바 표준 라이브러리에서의 활용
- String, Integer, LocalDateTime 등 불변 객체 활용
- 컬렉션 또한 Collections.unmodifiableList()와 같은 메서드를 통해 불변 컬렉션을 쉽게 만들 수 있어, 데이터 구조의 안전성을 확보할 수 있다.
'Java > 기본' 카테고리의 다른 글
[Java] Future (0) | 2024.11.08 |
---|---|
[Java] Java에서 final로 진짜_최종_최종_최종 불변 객체 만들기 (0) | 2024.10.31 |
[Java] 스트림(Stream) (0) | 2024.10.27 |
[Java] 표준 함수형 인터페이스 (0) | 2024.10.27 |
[Java] 메서드 참조 (0) | 2024.10.27 |