JAVA

JAVA 깊은 복제와 얕은 복제

euicheol0910 2025. 4. 28. 17:42

Java에서 깊은 복제(Deep Copy)와 얕은 복제(Shallow Copy)는 객체를 복사할 때 매우 중요한 개념입니다. Java는 기본적으로 객체를 참조(reference)로 다루기 때문에, 객체 복사 시 이 두 방식의 차이를 잘 이해해야 의도하지 않은 버그를 피할 수 있습니다.

 

1. 얕은 복제 (Shallow Copy)

 

얕은 복제는 객체의 필드 값만 복사하지만, 그 중 객체 타입(참조 타입) 필드는 참조 주소만 복사합니다.
즉, 복사된 객체 내부의 참조 타입 필드는 원본 객체와 동일한 객체를 가리킵니다.

 

 

class Address {
    String city;

    Address(String city) {
        this.city = city;
    }
}

class Person implements Cloneable {
    String name;
    Address address;

    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 얕은 복제
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("Seoul");
        Person p1 = new Person("Alice", addr);
        Person p2 = (Person) p1.clone(); // 얕은 복사

        p2.name = "Bob";
        p2.address.city = "Busan"; // 내부 참조된 객체를 수정

        System.out.println(p1.address.city); // Busan (⚠️ 원본도 바뀜!)
    }
}

 

 

 얕은 복사는 객체를 복사할 때, 그 안의 값 타입은 새로 복사되고, 참조 타입은 주소만 복사됩니다. 그래서 겉보기엔 다른 객체지만, 안에 포함된 참조형 필드들은 같은 객체를 공유합니다.

 

 예를 들어 A라는 사람이 있고, 그 사람의 주소 객체를 가진 복사본 B를 만들었다면, A와 B는 이름은 다르지만 주소는 같은 곳을 가리키고 있어요. 그래서 B가 주소를 바꾸면 A의 주소도 같이 바뀌게 됩니다. 즉, 겉만 다르고 속은 공유한다 — 이게 얕은 복사의 핵심입니다.

 

2. 깊은 복제 (Deep Copy)

 

깊은 복제는 객체의 모든 필드와, 그 안에 포함된 참조 타입 필드까지 재귀적으로 복사합니다.
복제된 객체는 원본과 완전히 독립적입니다.

 

Java는 기본적으로 깊은 복제를 자동으로 제공하지 않기 때문에 직접 구현해야 합니다. 대표적인 방법은 다음과 같습니다.

 

class Address implements Cloneable {
    String city;

    Address(String city) {
        this.city = city;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return new Address(this.city);
    }
}

class Person implements Cloneable {
    String name;
    Address address;

    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 얕은 복제 후 내부 객체도 별도로 복제
        Person copied = (Person) super.clone();
        copied.address = (Address) address.clone(); // 깊은 복제
        return copied;
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("Seoul");
        Person p1 = new Person("Alice", addr);
        Person p2 = (Person) p1.clone(); // 깊은 복사

        p2.address.city = "Busan";

        System.out.println(p1.address.city); // Seoul (원본 영향 X)
    }
}

 

Address 클래스에서는 clone() 호출 시 새로운 Address 객체를 생성해서 리턴합니다.
이로 인해 Person이 Address를 복사할 때 같은 주소가 아닌 새 주소 객체를 만들 수 있게 됩니다.

 

super.clone()으로 name과 address 참조를 복사합니다. (이건 얕은 복사)
그 후에 copied.address = (Address) address.clone(); 이 부분에서 주소 객체도 복제해서 따로 할당함으로써 깊은 복제를 완성합니다. 이제 p1.address와 p2.address는 서로 다른 객체입니다.

 

즉, 깊은 복제는 객체를 복사할 때, 그 안에 들어 있는 모든 값과 참조 객체까지 전부 새로 복사하는 방식이에요. 즉, 겉뿐 아니라 속까지 완전히 따로 만들어진 새로운 객체가 됩니다.

 

예를 들어 A라는 사람과 그 사람의 주소가 있을 때, 깊은 복제를 하면 이름은 물론 주소까지도 완전히 새 객체로 복사돼요. 그래서 복제본 B가 주소를 바꿔도 A의 주소는 전혀 영향을 받지 않습니다.

한마디로, 겉도 다르고 속도 다르다 — 이게 깊은 복사의 핵심입니다.

 

 

'JAVA' 카테고리의 다른 글

프록시 패턴과 AOP  (0) 2025.05.26
POJO  (4) 2025.05.21
JAVA 어노테이션  (0) 2025.04.28
자바의 제네릭에 대하여  (3) 2025.04.23
자바 인터페이스  (6) 2025.04.22