깊은복사, 얕은 복사


얕은 복사


얕은 복사란?

얕은 복사(Shallow Copy)는 주소값을 복사 한다는 의미이다.


예제로 살펴보기

public class ShallowCopy {

    public static void main(String[] args) {

        User user1 = new User("gilbert");
        User user2 = user1;

        System.out.println(user1.getName() + "[" + user1 + "]");
        System.out.println(user2.getName() + "[" + user2 + "]");

        user2.setName("kevin");
        System.out.println(user1.getName() + "[" + user1 + "]");
        System.out.println(user2.getName() + "[" + user2 + "]");
    }
}

class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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


출력결과


출력결과를 보면 참조 변수가 가리키는 메모리 번지는 동일하다.

여기서 주의할 점은 위의 예시에서도 알 수 있듯이, 얕은 복사의 경우 참조변수를 복사하는 것이기

때문에 결과적으로 힙영역에 있는 같은 인스턴스를 가리킨다. (동일한 주소를 참조)

따라서 둘(user1, user2) 중 하나에서 값을 변경하면 모두 값이 변하게 된다.


깊은 복사


깊은 복사란?

깊은 복사(Deep Copy)는 실제 값 을 새로운 메모리 공간에 복사하는 것을 의미한다.


깊은 복사 구현하기

[ Cloneable 인터페이스 구현 ]

public class DeepCopy {

    public static void main(String[] args) throws CloneNotSupportedException {

        Player p1 = new Player("gilbert", 28);
        Player p2 = (Player) p1.clone();

        // isSameAs : 메모리 주소 비교
        assertThat(p1).isSameAs(p2);    

        // isEqualTo : 객체의 데이터 비교
        assertThat(p1.getName()).isEqualTo(p2.getName());
        assertThat(p1.getAge()).isEqualTo(p2.getAge());
    }
}

@Getter @Setter
@AllArgsConstructor
class Player implements Cloneable{

    private String name;
    private int age;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
출력결과


출력결과에서 볼 수 있듯 서로 다른 주소를 참조한다.

따라서 값을 변경해도 해당 참조변수가 가리키는 메모리의 데이터만 바뀌게 된다.


[ 복사 팩토리 방식 ]

public class DeepCopy {

    public static void main(String[] args) {

        Player p1 = new Player("gilbert", 28);
        Player p2 = Player.deepCopyPlayer(p1);

        assertThat(p1).isNotSameAs(p2);
        assertThat(p1.getName()).isNotEqualTo(p2.getName());
        assertThat(p1.getAge()).isNotEqualTo(p2.getAge());
    }
}

@Getter @Setter
@AllArgsConstructor
class Player {

    private String name;
    private int age;

    public Player(Player original) {
        name = original.getName();
        age = original.getAge();
    }

    public static Player deepCopyPlayer(Player original) {
        return new Player(original);
    }
}


Cloneable 인터페이스를 구현했을 때와 같은 결과를 보여준다.


왜 복사 생성자를 사용해야하나?