[ 객체 복사 ]
설명
Java에서 객체를 복사하는 방법에 대해서 알아보자!
개발을 할 때, List나 인스턴스 등의 객체를 복사해서 사용해야하는 상황은 빈번히 일어난다. (원본 데이터 보호, 불변성 유지 등,,)
그런데... 객체를 복사할 때는 주의해야할 점이 있다. 아래의 예시와 함께 알아보자.
예시
Person person = new Person("kangrae", 2001);
Person copiedPerson = person;
copiedPerson.setName("gyunho");
copiedPerson.setAge(2024);
위와 같이 person을 복사하고, 복사된 copiedPerson의 name을 바꾸면 어떻게 될까? 출력하여 확인해보자.
System.out.println(person); // gyunho, 2024
System.out.println(copiedPerson); // gyunho, 2024
우리는 원본 데이터를 보호하기 위해서 객체를 복사한 뒤, 복사된 객체를 수정했다. 그런데 왜.... person의 name도 함께 바뀐 것일까?
원인은 person과 copiedPerson은 동일한 객체를 참조하기 때문이다! 그렇다면 어떻게 복사를 해야하는가....??
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
}
직전의 예시처럼 복사가 잘 되는 듯 했으나.... book1의 author가 왜 kangrae에서 gyunho로 변경되었을까??
왜냐하면.... shallowCopy()에서 새로운 Book객체를 만들 때, author객체를 참조했기 때문이다. 이것은 여전히 얕은 복사이다!
그렇다면 왜 Person의 예시에서는 복사가 잘 되는 것 처럼 보였을까?
Person Class의 name은 String 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")를 해도, book2의 author는 여전히 gyunho이다! 이것이 깊은 복사이다!!
[ 한 줄 요약 ]
얕은 복사 : 객체의 참조만 복사하여 원본 객체와 복사된 객체가 동일한 참조를 공유하는 방식
깊은 복사 : 객체와 그 내부의 참조 객체까지 모두 복사하여, 원본 객체와 복사된 객체가 서로 독립적인 객체를 참조하는 방식
[ 더 알아보기 ]
clone() + convariant return type
Builder 패턴
Serialization
[ 참고 자료 ]
'Java 기술 노트' 카테고리의 다른 글
Annotations (0) | 2025.03.18 |
---|---|
Garbage Collector (0) | 2025.03.11 |
Wrapper 객체? (0) | 2025.03.09 |