얕은 복사 (Shallow Copy)

  • 객체의 1차 필드(속성)만 복사하고 그 필드가 또 다른 객체를 참조하고 있으면 참조 주소만 복사함
  • 겉껍질만 복사하고 안의 내용은 원본과 공유
  • 객체 안에 있는 참조 타입 필드는 원본과 복사본이 같은 객체를 가리킴
  • 하나를 바꾸면 다른 하나도 바뀔 수 있음
class Person {
    String name;
    Address address;
}
 
class Address {
    String city;
}
 
Person p1 = new Person();
p1.name = "Alice";
p1.address = new Address();
p1.address.city = "Seoul";
 
// 얕은 복사
Person p2 = p1;  // 참조만 복사
p2.address.city = "Busan";
 
System.out.println(p1.address.city);  // "Busan"

깊은 복사 (Deep Copy)

  • 객체를 복사하면서 참조 타입 필드도 새로운 객체로 생성해서 복사
  • 객체 구조 전체를 재귀적으로 복사
  • 원본과 복사본이 완전히 독립적인 객체가 됨
  • 하나를 바꿔도 다른 하나에 영향 없음
class Person implements Cloneable {
    String name;
    Address address;
 
    public Person clone() {
        Person cloned = new Person();
        cloned.name = this.name;
        cloned.address = new Address();
        cloned.address.city = this.address.city;
        return cloned;
    }
}
 
class Address {
    String city;
}
 
Person p1 = new Person();
p1.name = "Alice";
p1.address = new Address();
p1.address.city = "Seoul";
 
// 깊은 복사
Person p2 = p1.clone();
p2.address.city = "Busan";
 
System.out.println(p1.address.city);  // "Seoul"
항목얕은 복사 (Shallow Copy)깊은 복사 (Deep Copy)
기본 방식참조만 복사객체를 새로 생성해 복사
참조 타입 필드공유됨독립적
메모리 사용적음많음
독립성낮음높음

.clone() 메서드

  • 기본적으로 얕은 복사
  • clone()을 오버라이드해서 내부 객체도 새로 생성해야 깊은 복사 가능
  • Cloneable 인터페이스를 Implements해야 함 CloneNotSupportedException 발생

clone()을 왜 오버라이딩해야 하나요?

  • clone()은 Object 클래스에 protected로 정의되어 있기 때문에 직접 사용하려면 오버라이딩해서 public으로 변경해야 함
  • 깊은 복사를 구현하려면 내부 로직도 커스터마이징 해야 하므로 오버라이딩 필요

빌더 패턴

  • 필드를 그대로 넘기면 얕은 복사
  • 내부 객체를 새로 생성하면 깊은 복사
  • 유연하고 가독성 좋음, 불변 객체에 유리

복사 생성자 (Copy Constructor)

  • 얕은 복사 / 깊은 복사 선택 가능
  • 선택적으로 구현
  • 자주 사용되며 코드 명확

직렬화 / 역직렬화

  • 기본적으로 깊은 복사
  • 느리지만 객체 전체를 통째로 복제할 때 사용
상황추천 방식
단순한 복사, 속도 중요clone() (주의해서)
불변 객체 만들고 싶을 때빌더 패턴
코드 가독성 중요할 때빌더 or 복사 생성자
객체가 복잡하고 깊은 구조일 때빌더 / 복사 생성자 / 직렬화 방식

적용 사례

  • 얕은 복사 DTO나 설정값처럼 객체 구조가 단순하고 공유해도 문제없는 경우 주로 사용, 컨트롤러에서 서비스로 전달되는 객체는 불변으로 설계되어 있어 얕은 복사로 충분

  • 깊은 복사 내부 상태까지 완전히 분리된 객체가 필요한 경우 사용

  • 멀티스레드 환경에서 공유된 객체가 상태 변경시 문제를 일으킬 수 있기 때문에 깊은 복사를 통해 안전하게 분리해서 사용

Ref