본문 바로가기
프로그래밍/JAVA

Java 불변 객체(Immutable Object)란? 불변객체 vs 가변객체 정리

by 곰 옥수수 2025. 3. 3.
728x90
반응형

불변 객체(Immutable Object)란? 

한 번 생성되면 내부 상태를 변경할 수 없는 객체를 의미합니다. 즉, 객체의 필드 값이 생성 이후 절대 변경되지 않습니다.

1) 불변 객체의 특징

불변 객체를 만들기 위해서는 다음과 같은 원칙을 따라야 합니다.

1 모든 필드는 final로 선언

  • final 키워드를 사용하면 객체가 생성된 후 해당 필드를 변경할 수 없습니다.
public final class ImmutablePerson {
    private final String name;
    private final int age;
}

2. 객체를 변경하는 setter 메서드를 제공하지 않음

  • 불변 객체는 객체의 값을 변경할 수 없도록 setter 메서드를 제공하지 않습니다.
public void setName(String name) {  // 이런 메서드는 만들면 안 됨!
    this.name = name;
}

3. 생성자에서 모든 필드를 초기화

  • 객체가 생성될 때 필드 값을 설정하고, 이후에는 변경할 수 없도록 합니다.
public ImmutablePerson(String name, int age) {
    this.name = name;
    this.age = age;
}

4. 객체의 변경이 필요하면 새로운 객체를 반환

  • 기존 객체의 값을 변경할 수 없기 때문에, 변경이 필요할 경우 새로운 객체를 생성하여 반환해야 합니다.
public ImmutablePerson withAge(int newAge) {
    return new ImmutablePerson(this.name, newAge);
}

5. clone()을 제공하지 않음

  • clone() 메서드는 얕은 복사(Shallow Copy) 를 수행할 수 있기 때문에, 불변 객체에서는 사용하지 않는 것이 일반적입니다.

2) 불변 객체의 장점

1. 멀티스레드 환경에서 안전(Thread-Safe)

  • 불변 객체는 동기화(Synchronization) 를 할 필요가 없습니다.
  • 여러 스레드에서 동시에 읽기(read)만 수행하고, 값이 변경되지 않기 때문입니다.
ImmutablePerson person = new ImmutablePerson("Alice", 30);
// 여러 스레드에서 동시에 person을 사용해도 안전함.

2. 객체의 상태가 변하지 않아 예측 가능

  • 코드의 예측 가능성이 높아져 디버깅이 쉬워지고 유지보수성이 향상됩니다.
ImmutablePerson person1 = new ImmutablePerson("Bob", 25);
ImmutablePerson person2 = person1.withAge(30);

System.out.println(person1); // ImmutablePerson{name='Bob', age=25}
System.out.println(person2); // ImmutablePerson{name='Bob', age=30}

→ person1은 변하지 않고, person2라는 새로운 객체가 생성됨.


3. 불변 객체는 해시 기반 컬렉션(HashMap, HashSet)에서 안전하게 사용 가능

  • 불변 객체는 hashCode() 값이 변하지 않기 때문에, HashMap 또는 HashSet에 저장할 때 안전합니다.
Map<ImmutablePerson, String> map = new HashMap<>();
ImmutablePerson person = new ImmutablePerson("Charlie", 40);

map.put(person, "Engineer");

// person의 값이 변하지 않기 때문에, 해시 값도 변하지 않아 안전함.
System.out.println(map.get(person)); // Engineer

3) 불변 객체의 단점

1. 값이 변경될 때마다 새로운 객체가 생성됨

  • 값이 변할 때마다 새로운 객체가 생성되므로, 메모리 사용량이 증가할 수 있습니다.
String str1 = "Hello";
String str2 = str1.concat(" World"); // 새로운 객체 생성

System.out.println(str1); // Hello
System.out.println(str2); // Hello World

→ 기존 str1은 변하지 않고, str2라는 새로운 객체가 생성됨.


2. 성능 저하

  • 자주 변경되는 데이터를 다룰 경우 객체 생성 비용이 증가할 수 있습니다.
  • 예를 들어 String 대신 StringBuilder를 사용하는 것이 성능적으로 유리할 수 있음.
// 불변 객체(String) 사용
String s = "Hello";
s += " World";  // 새로운 String 객체 생성

// 가변 객체(StringBuilder) 사용
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");  // 기존 객체에서 값 변경 가능 (성능 개선)

3. 불변 객체 만들기 (예제 코드)

불변 객체 예제

public final class ImmutablePerson {
    private final String name;
    private final int age;

    // 생성자를 통해 값 초기화
    public ImmutablePerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter만 제공 (Setter 없음)
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    // 변경이 필요하면 새로운 객체를 반환
    public ImmutablePerson withAge(int newAge) {
        return new ImmutablePerson(this.name, newAge);
    }

    @Override
    public String toString() {
        return "ImmutablePerson{name='" + name + "', age=" + age + "}";
    }
}

사용 예제

public class Main {
    public static void main(String[] args) {
        ImmutablePerson person1 = new ImmutablePerson("John", 25);
        System.out.println(person1); // ImmutablePerson{name='John', age=25}

        ImmutablePerson person2 = person1.withAge(30); // 새로운 객체 생성
        System.out.println(person2); // ImmutablePerson{name='John', age=30}

        // 기존 객체는 변경되지 않음
        System.out.println(person1); // ImmutablePerson{name='John', age=25}
    }
}

가변 객체(Mutable Object)란?

객체 내부 상태를 변경할 수 있는 객체를 의미합니다.

가변 객체의 특징

  1. 필드 값 변경 가능 → setter 메서드 제공
  2. 객체의 상태를 변경할 수 있음
  3. 동기화 문제 발생 가능 → 여러 스레드에서 동시에 접근하면 일관성이 깨질 가능성이 있음

가변 객체의 장점

  • 객체를 계속 수정할 수 있어 성능이 좋음 → 새로운 객체를 생성할 필요 없음
  • 메모리 사용량 절약 → 동일 객체를 재사용 가능
  • 코드가 유연함 → 필요에 따라 값을 자유롭게 변경 가능

가변 객체의 단점

  • Thread-Safety 문제 → 여러 스레드에서 수정할 경우 동기화 필요
  • Side Effect 발생 가능 → 참조를 공유할 경우 예측하지 못한 값 변경 발생 가능
  • 디버깅이 어려움 → 상태 변경이 많으면 추적이 어려움

가변 객체 예제 코드

public class MutablePerson {
    private String name;
    private int age;

    // 생성자
    public MutablePerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter & Setter 제공
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "MutablePerson{name='" + name + "', age=" + age + "}";
    }
}

사용 예시

public class Main {
    public static void main(String[] args) {
        MutablePerson person = new MutablePerson("Alice", 20);
        System.out.println(person); // MutablePerson{name='Alice', age=20}

        // 값 변경
        person.setAge(25);
        System.out.println(person); // MutablePerson{name='Alice', age=25}
    }
}

→ person 객체가 그대로 유지되며 내부 상태만 변경됨


불변 객체 vs. 가변 객체 비교

비교  불변 객체 (Immutable) 가변 객체 (Mutable)
상태 변경 가능 여부 불가능 (값 변경 시 새로운 객체 생성) 가능 (Setter를 통해 변경 가능)
Thread-Safe 안전 동기화 필요
메모리 사용 증가 (새로운 객체 생성) 절약 (기존 객체 수정)
성능 상대적으로 느림 (새로운 객체 생성 비용) 빠름 (기존 객체 수정 가능)
예제 String, Integer, LocalDateTime StringBuilder, ArrayList

불변 객체를 사용할 때의 대표적인 Java 클래스

Java에서는 몇몇 클래스가 기본적으로 불변 객체(Immutable Object) 로 설계되어 있습니다.

불변 객체의 대표적인 예

Java에는 불변 객체로 설계된 클래스들이 있습니다.

 

클래스 설명
String 문자열 변경 불가능
Integer, Double, Float, Long, Short, Byte, Boolean Wrapper 클래스
LocalDate, LocalDateTime, Instant 날짜 관련 클래스 (Java 8+)
BigDecimal, BigInteger 수학 연산을 위한 클래스

 

예제: String 클래스는 불변 객체

 
String s1 = "Hello";
String s2 = s1.concat(" World"); // 새로운 객체 생성

System.out.println(s1); // Hello
System.out.println(s2); // Hello World

언제 불변 객체를 사용해야 할까?

  • 멀티스레드 환경에서 데이터 무결성을 유지하고 싶을 때
  • 객체의 불변성을 보장해야 할 때 (데이터 무결성이 중요할 때)
  • 값이 변경될 필요가 없는 경우
  • 해시 기반 컬렉션(HashMap, HashSet)에서 안전하게 사용해야 할 때

언제 가변 객체를 사용해야 할까?

  • 값을 자주 변경해야 할 때
  • 성능이 중요한 경우 (객체 생성 비용 절감)
  • 동기화 이슈를 직접 관리할 수 있을 때

결론

  • 불변 객체(Immutable Object)안전성예측 가능성이 높지만 성능이 낮음.
  • 가변 객체(Mutable Object)유연성성능이 뛰어나지만 Thread-Safety 문제가 발생할 수 있음.
728x90
반응형

댓글