Java 기술 노트

얕은 복사와 깊은 복사

kng0501 2025. 3. 10. 02:06

[ 객체 복사 ]


설명

 Java에서 객체를 복사하는 방법에 대해서 알아보자!

개발을 할 때, List나 인스턴스 등의 객체를 복사해서 사용해야하는 상황은 빈번히 일어난다. (원본 데이터 보호, 불변성 유지 등,,)

그런데... 객체를 복사할 때는 주의해야할 점이 있다. 아래의 예시와 함께 알아보자.

 

예시

Person person = new Person("kangrae", 2001);
Person copiedPerson = person;

copiedPerson.setName("gyunho");
copiedPerson.setAge(2024);

 

 위와 같이 person을 복사하고, 복사된 copiedPersonname을 바꾸면 어떻게 될까? 출력하여 확인해보자.

System.out.println(person);        // gyunho, 2024
System.out.println(copiedPerson);  // gyunho, 2024

 

 우리는 원본 데이터를 보호하기 위해서 객체를 복사한 뒤, 복사된 객체를 수정했다. 그런데 왜.... person의 name도 함께 바뀐 것일까?

  원인은 personcopiedPerson동일한 객체를 참조하기 때문이다! 그렇다면 어떻게 복사를 해야하는가....??

Java에서 복사는 얕은 복사깊은 복사가 존재한다. 이제 차근차근 알아가보자.

 

 

[ 얕은 복사 ]


설명

 앞선 얕은 복사 예시를 통해서 우리는 객체의 필드를 복사해야한다는 것을 알게 되었다. 그렇다면 아래의 Person 객체를 복사해보자!

Person person = new Person("kangrae", 2001);
Person copiedPerson = new Person(person.getName(), person.getAge());

copiedPerson.setName("gyunho");
copiedPerson.setAge(2024);

System.out.println(person);        // kangrae, 2001
System.out.println(copiedPerson);  // gyunho, 2024

 

 복사가 잘 되는 모습을 볼 수 있다!! 사실 아님..

 다른 예시로도 확인해보자.

public class Author {

    private String name;

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

public class Book {

    private String name;
    private Author author;

    public Book(String name, Author author) {
        this.name = name;
        this.author = author;
    }

    public Book shallowCopy() {
        return new Book(this.name, this.author);
    }
    
    public void changeAuthor(String name) {
        author.setName(name);
    }
}

public static void main(String[] args) {
    Author author1 = new Author("kangrae");
    Book book1 = new Book("Wrapper", author1);

    Book shallowCopyBook = book1.shallowCopy();
    shallowCopyBook.changeAuthor("gyunho");

    System.out.println("Original  book1: " + book1);           // Wrapper, gyunho
    System.out.println("Shallow cp book: " + shallowCopyBook); // Wrapper, gyunho
}

 

 직전의 예시처럼 복사가 잘 되는 듯 했으나.... book1author가 왜 kangrae에서 gyunho변경되었을까??

왜냐하면.... shallowCopy()에서 새로운 Book객체를 만들 때, author객체를 참조했기 때문이다. 이것은 여전히 얕은 복사이다!

 

 그렇다면 왜 Person의 예시에서는 복사가 잘 되는 것 처럼 보였을까?
Person ClassnameString type이다. String은 불변 객체로서, 값을 변경하려고 하면 새로운 String 객체가 생성된다. 따라서 name필드를 수정했을 때 새로운 String 객체가 생성되었기 때문에 복사가 잘 되는듯 보인 것이다!

 

 흠.. 그럼 얕은 복사쓸모 없는 것인가? 뒤에 설명될 깊은 복사만이 실제사용되는가?

그런것은 아니다. 얕은 복사속도메모리 효율이 좋으며, 아래와 같은 상황에 사용된다.

  • 원본 객체연결을 유지하고 싶을 때
  • 객체 내에 참조 타입 필드가 없고 값 타입(int, double등,, )만 있는 경우
  • 복사된 객체필드 값이 변경되지 않을 것이 확실 할 때

 위와 같은 상황들에 대해서는 얕은 복사를 해도 충분하다. 하지만 원본 데이터를 보존해야 하는 등, 불변성을 유지해야하는 경우에는 깊은 복사가 필요하다. 그럼 이제 깊은 복사에 대해서 알아보자.

 

 

[ 깊은 복사 ]


설명

 얕은 복사에서는 할 수 없었던 원본 보존을 지원하는 깊은 복사에 대해서 알아보자!

깊은 복사를 하기 위해서는 객체 내부의 필드에 대해서 각각 새로운 객체를 생성해주어야 한다. 코드와 함께 살펴보자.

public class Author {

    private String name;

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

public class Book {

    private String name;
    private Author author;

    public Book(String name, Author author) {
        this.name = name;
        this.author = author;
    }

    public Book deepCopy() {
        Author copiedAuthor = new Author(this.author.getName());
        return new Book(this.name, copiedAuthor);
    }

    public void changeAuthor(String name) {
        author.setName(name);
    }
}

public static void main(String[] args) {
    Author author2 = new Author("gyunho");
    Book book2 = new Book("DI", author2);

    Book deepCopyBook = book2.deepCopy();
    deepCopyBook.changeAuthor("kangrae");

    System.out.println("Original book2: " + book2);         // DI, gyunho
    System.out.println("Deep cp   book: " + deepCopyBook);  // DI, kangrae
}

 

 얕은 복사에서의 shallowCopy()와는 달리, deepCopy()에서는 copiedAuthor라는 새로운 객체를 생성하고 있는 모습을 볼 수 있다. 따라서 복사된 deepCopyBook객체를 받아서 changeAuthor("kangrae")를 해도, book2author는 여전히 gyunho이다! 이것이 깊은 복사이다!!

 

 

[ 한 줄 요약 ]


얕은 복사 : 객체의 참조만 복사하여 원본 객체복사된 객체가 동일한 참조를 공유하는 방식

깊은 복사 : 객체와 그 내부의 참조 객체까지 모두 복사하여, 원본 객체복사된 객체서로 독립적인 객체를 참조하는 방식

 

 

[ 더 알아보기 ]


 clone() + convariant return type

 Builder 패턴

 Serialization

 

 

[ 참고 자료 ]


☕ 자바 clone 메서드 재정의 (얕은 복사 & 깊은 복사)

매일메일 - 얕은 복사와 깊은 복사에 대해서 설명해주세요.

'Java 기술 노트' 카테고리의 다른 글

Annotations  (0) 2025.03.18
Garbage Collector  (0) 2025.03.11
Wrapper 객체?  (0) 2025.03.09