얕은 복사로 객체를 넘겨주게 되면 주소를 공유하게 되어 실제값 변경에 영향을 미친다는 것을 알게 되었다. 이후 문제를 푸는 과정에서 객체를 반환할 때 list 대신 new ArrayList<>(list)를 반환하는 등 객체를 새로 생성해서 반환하도록 구현했다. 이번 기회에 얕은 복사가 일어나는 동작 원리에 궁금증이 생겨 두 복사의 차이점을 정리하고자 한다.
얕은 복사 (Shallow Copy)
먼저 테스트를 위해 학생 클래스를 만들어 주었다.
이름과 학년을 멤버 변수로 가지며, 이름과 학년을 변경할 수 있는 메서드를 가지고 있다.
학생 타입의 jiyoon 객체를 먼저 생성해준 뒤 copy에 jiyoon을 대입해 주어 얕은 복사를 진행하였다.
이후 copy를 통해 이름과 학년을 변경하면 어떻게 될까?
jiyoon과 copy가 필드가 모두 변경된 것을 확인할 수 있다.
copy로 이름과 학년을 변경하였는데, 왜 두 jiyoon의 값까지 변경된 것일까?
그 이유는 주소를 공유하고 있기 때문이다.
즉, copy는 jiyoon의 값을 복사한 것이 아니라 참조값을 복사한 것이다.
위 사진 처럼 jiyoon과 copy는 하나의 참조값을 공유하고 있다.
여기서 둘 중 하나의 객체를 통해 이름이나 학년을 변경하게 될 경우,
참조하는 실제값이 수정되며, 이 값들에 접근하게 되는 것이다.
이와 같은 얕은 복사를 사용하는 경우도 있지만, 참조값이 아니라 값만 복사하여 사용해야 하는 경우도 존재한다. 그리고 그러한 경우에 사용해야 하는 것이 깊은 복사(Deep Copy)이다.
깊은 복사 (Deep Copy)
깊은 복사를 구현하는 방법에는 2가지가 있다. Cloneable을 구현하여 clone()을 재정의하는 방법도 있지만, final 인스턴스 또는 배열이 아닌 경우 권장하는 방법이 아니기 때문에 제외하였다.
1. 복사 생성자 또는 복사 팩터리를 이용하여 복사하기
먼저 Student 클래스에 복사 생성자와 복사 팩터리를 생성해 주었다.
이때 복사 팩터리에서 기본 생성자를 사용하기 때문에 기본 생성자 역시 따로 정의해 주어야 한다.
copy1은 복사 생성자를 이용해 복사하고, copy2는 복사 팩터리를 이용해 복사하였다.
바로 객체들의 참조값과 실제값을 출력해보면,
새로운 객체를 생성하여 참조하기 때문에 값만 복사된 것을 확인할 수 있다.
여기서 값을 변경하게 되면,
복사 객체들의 실제값만 변경되는 것을 확인할 수 있다.
2. 직접 객체를 생성하여 복사하기
아예 메소드를 정의하여 객체를 직접 복사해주는 방식이다.
하지만 setter는 구글 코드 컨벤션에서 지양하는 방법이므로 첫번째 방법을 사용하는 것이 좋다.
'Dev > Java' 카테고리의 다른 글
[Java] Web server failed to start. Port 5000 was already in use. (MacOS) (1) | 2024.01.24 |
---|---|
[Java] Checked Exception vs. UnChecked Exception (1) | 2023.09.12 |
[SOLID] 좋은 객체지향 설계의 5가지 원칙 (0) | 2023.09.07 |
[Java] isPresent() vs. ifPresent() (0) | 2023.09.01 |
[Java] pickNumberInRange ArrayIndexOutOfBoundsException (0) | 2023.08.23 |