[일문일답][Java] 깊은 복사(Deep Copy)와 얕은 복사(Shallow Copy)
2022. 8. 24. 16:48ㆍ일문일답/Java
깊은 복사 : '실제 값'을 새로운 메모리 공간에 복사하는 것을 의미 (실제값이 다르다.)
얕은 복사 : '주소 값'을 복사한다. (즉, 참조하고 있는 실제값은 같다.)
얕은 복사의 경우 주소 값을 복사하기 때문에, 참조하고 있는 실제 값은 같다.
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
static class Human implements Cloneable {
private String name;
private int age;
// 복사 생성자
public Human(Human human) {
this.name = human.name;
this.age = human.age;
}
// 복사 팩터리
public static Human newInstance(Human human) {
Human instance = new Human();
instance.setName(human.getName());
instance.setAge(human.getAge());
return instance;
}
@Override
protected Human clone() throws CloneNotSupportedException {
return (Human) super.clone();
}
}
@Test
@DisplayName("얕은 복사")
void shallowCopy() {
Human h1 = new Human("lee", 20);
Human h2 = h1;
Assertions.assertEquals(h1.hashCode(), h2.hashCode());
h1.setAge(21);
Assertions.assertEquals(21, h2.getAge());
}
@Test
@DisplayName("깊은 복사 1")
void deepCopy1() {
Human h1 = new Human("lee", 20);
Human h2 = new Human(h1);
Human h3 = new Human(h1);
Assertions.assertNotEquals(h1.hashCode(), h2.hashCode());
Assertions.assertNotEquals(h1.hashCode(), h3.hashCode());
h1.setAge(21);
Assertions.assertNotEquals(21, h2.getAge());
Assertions.assertNotEquals(21, h3.getAge());
}
@Test
@DisplayName("깊은 복사2")
void deepCopy2() {
Human h1 = new Human("lee", 20);
Human h2 = new Human();
h2.setAge(h1.getAge());
h2.setName(h1.getName());
Assertions.assertNotEquals(h1.hashCode(), h2.hashCode());
h1.setAge(21);
Assertions.assertNotEquals(21, h2.getAge());
}
@Test
@DisplayName("깊은 복사3")
void deepCopy3() throws CloneNotSupportedException {
Human h1 = new Human("lee", 20);
Human h2 = h1.clone();
Assertions.assertNotEquals(h1.hashCode(), h2.hashCode());
h1.setAge(21);
Assertions.assertNotEquals(21, h2.getAge());
}
다음과 같이 테스트 코드를 작성할 수 있다.
얕은 복사의 경우,
h1 인스턴스를 생성하면 stack에 참조값, heap에 실제값이 올라간다.
그리고, h2 인스턴스를 생성할 때는 h1의 참조값이 저장된다.
따라서, h1 인스턴스의 값을 변경한다면 h2도 동일한 객체를 참조하고 있기 때문에 h2.getAge()를 했을 때 바뀐 값이 도출될 수 밖에 없다.
깊은 복사의 경우,
깊은 복사를 구현하는 방식은 3가지가 주요 방법이라고 할 수 있다.
- 복사 생성자 또는 복사 팩터리를 이용하여 복사한다.
- 직접 객체를 생성하여 복사한다.
- Cloneable 인터페이스를 구현하여 clone() 메서드를 재정의한다. (final 인스턴스 또는 배열이 아닌 경우 사용을 권하지 않음)
총 3가지의 테스트이지만 모두 구현하는 내용은 같다.
h1 인스턴스를 생성한 후, h2 인스턴스를 생성한 뒤 얕은 복사와의 차이점은 Heap 영역에서 동일한 값을 가리키는 것이 아니라 그대로 복사된 또 다른 객체를 가리킨다.
따라서, h1 인스턴스의 값을 변경한다고 해도 h2가 가리키는 실제값에는 영향을 줄 수 없다. (관련없는 독립된 값이기 때문)
'일문일답 > Java' 카테고리의 다른 글
[일문일답][Java] Iterator 인터페이스와 Iterable 인터페이스 (0) | 2022.08.25 |
---|---|
[일문일답][Java] Reflection의 개념 및 사용 방법 (0) | 2022.08.24 |
[일문일답][Java] 'Call by value'와 'Call by reference'의 차이 (0) | 2022.08.24 |
[일문일답][Java] 가비지 컬렉션 - 2 (0) | 2022.08.24 |
[일문일답][Java] 가비지 컬렉션(GC) - 1 (0) | 2022.08.24 |