✅ Java에서 말하는 복사란?
복사는 원본을 그대로 둔 채, 동일한 데이터를 가진 새로운 객체를 생성하는 것입니다. 그래서 Java에서 복사는 clone() 메소드를 반드시 사용해야 합니다. 해당 메소드를 사용하지 않고 단순히 참조 변수에 기존 변수를 대입하는 것은 복사가 아닙니다. 단지 대입에 불과합니다. 반드시 clone() 메소드를 통해서 새로운 객체를 생성해야합니다. 객체 안에 필드의 참조 변수를 원본 참조 객체를 참조할지, 원본 참조 변수를 토대로 새로운 참조 객체를 생성할지 여부에 따라 얕은 복사와 깊은 복사로 나뉘는 것입니다.
➡️ Java에서 복사에 필요한 조건은 다음과 같습니다.
- Clonealbe Interface를 구현해야 한다.
Object.clone()
메소드를 오버라이드해서 정의해야 한다.
- 객체 필드의 참조 객체를 주소 복사를 할지, 새로운 참조 객체를 생성할지의 여부에 따라서 얕은 복사와 깊은 복사로 나뉘게 된다.
//원본 객체를 참조하고 있는 참조 변수
ShallowCopyClass shallowCopy = new ShallowCopyClass(5, mutableObject);
//다른 참조 변수에 원본 참조 변수를 대입하여 원본 객체 참조, 복사x
ShallowCopyClass notShallowCopy = shallowCopy;
// clone() 메소드를 사용한 원본 객체 복사, 새로운 객체 생성.
ShallowCopyClass shallowCopyClone = (ShallowCopyClass) shallowCopy.clone();
❗참조 변수에 원본 객체를 참조하는 변수를 대입하는 것으로 얕은 복사를 설명하고 있는 경우가 많습니다. 유의하시길 바랍니다. clone()
메서드를 사용하지 않으면 복사가 아닙니다.
MutableObject mutableObject = new MutableObject(10);
ShallowCopyClass shallowCopy = new ShallowCopyClass(5, mutableObject);
ShallowCopyClass shallowCopyClone = (ShallowCopyClass) shallowCopy.clone();
shallowCopyClone.setValue(50);
shallowCopy.setValue(100);
왜 clone()
메소드를 사용한 것만 복사인지 살펴보겠습니다.
shallowCopyClass는 Clonable 인터페이스를 구현하고 clone()
메소드를 재정의했습니다.
notShallowCopy는 단순히 shallowCopy 참조 변수를 대입하여 원본 객체의 주소를 받고 있습니다.
clone()
메소드를 통해 객체를 복사한 경우, 필드의 값을 변경하면 복사한 객체의 필드의 값만 변경됩니다. shallowCopyClone의 Value가 50으로 변경된 것을 볼 수 있습니다.
그러나 shallowCopy의 값을 변경한 경우, notShallowCopy 클래스의 Value 값도 100으로 함께 변경된 것을 볼 수 있습니다. shallowcopy 참조 변수와 notShallowCopy 참조 변수는 동일한 객체를 참조하고 있기 때문입니다. 이는 단순히 원본 객체의 참조 주소를 대입한 것이지 복사가 아닙니다. 단순히 대입으로 shallowCopy를 설명하고 있는 것은 잘못된 것입니다. 반드시 clone() 메소드를 통해 객체가 새로 생성되는 복사가 이루어져야 합니다.
clone()
메소드를 통해 객체의 복사가 이루어졌을 때, Java에서 말하는 객체 복사, 인스턴스를 복사하는 방법을 얕은 복사와 깊은 복사로 나눌 수 있습니다. 각각의 방법은 내부의 데이터를 어떻게 복사할지, 더 정확하게 말하자면 필드로 가지고 있는 참조 객체의 주소만 복사할지, 새로운 인스턴스를 생성할지의 여부에 따라 달라집니다.
얕은 복사와 깊은 복사에 대해서 알아보기 전에 Cloneable 인터페이스에 대해서 먼저 알아보겠습니다.
🔹Cloneable Interface
Cloneable 인터페이스는 자바에서 복제 가능한 객체를 표시하기 위해 사용되는 마커 인터페이스입니다. 마커 인터페이스란, 메서드나 필드를 갖지 않고 단지 특정 기능 또는 속성을 가진다는 것을 나타내는 인터페이스입니다. 실제로 해당 Cloneable 인터페이스를 살펴보면 어떠한 추상메소드도 정의되어 있지 않습니다.
Cloneable 인터페이스는 복제가 지원되는 객체임을 나타내기 위해 클래스에서 구현됩니다. Cloneable 인터페이스를 구현하는 클래스는 복제 기능을 사용할 수 있습니다. 그러나 Cloneable 인터페이스는 메서드를 선언하지 않기 때문에 별도의 메서드를 구현해야 하는 것은 아닙니다. 대신, Cloneable 인터페이스를 구현한 클래스는 Object 클래스의 clone()
메서드를 호출할 수 있습니다. clone()
메서드는 Object 클래스의 native 메소드이며 이는 JVM을 통해서 사용할 수 있게 됩니다.
clone() 메서드는 Cloneable 인터페이스를 구현한 클래스에서 상속받은 메서드로, 객체의 복제를 수행합니다. clone() 메서드는 protected 접근 제어자를 가지고 있으므로, 해당 클래스에서 직접 호출할 수 있거나 하위 클래스에서 호출할 수 있습니다.
clone()
메서드를 호출하기 전에는 Cloneable 인터페이스를 구현한지 확인해야 합니다. 그렇지 않으면 CloneNotSupportedException이 발생할 수 있습니다. Cloneable 인터페이스는 자바에서 복제 기능을 사용하기 위한 표시 역할을 하며, 이를 구현한 클래스는 객체의 복제를 지원합니다. 그러나 깊은 복사와 같은 세부 구현은 클래스에서 직접 처리해야 합니다.
✅ 얕은 복사(ShallowCopy)
Java에서 복사를 하기 위해선 반드시 Cloneable 인터페이스를 구현하고, Object.clone()
메소드를 오버라이드 해야 한다고 했습니다. 이제 얕은 복사에 대해서 알아보겠습니다.
- MutableObject
public class MutableObject implements Cloneable { private int data; public MutableObject(int data) { this.data = data; } public int getData() { return data; } public void setData(int data) { this.data = data; } @Override //자동으로 Object 클래스를 상속함. protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
- ShallowCopy
package com.jhcode.copyobjects; public class ShallowCopyClass implements Cloneable { private int value; private MutableObject mutableObject; public ShallowCopyClass(int value, MutableObject mutableObject) { this.value = value; this.mutableObject = mutableObject; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } public MutableObject getMutableObject() { return this.mutableObject; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
ShallowCopy 클래스는 Cloneable 인터페이스를 구현하고,
Object.clone()
메소드를 재정의해서 복사를 구현할 수 있습니다. 재정의했을 때 super(Object 클래스)의 clone() 메소드를 단순히 호출하는 것만 했으므로 ShallowCopy 클래스 타입의 객체의 필드로 MutableObject의 참조 주소를 복사합니다. 즉, 새로운 객체를 생성해서 필드로 가지는 것이 아니라 원본 객체가 참조하는 필드의 주소를 복사해서 똑같이 참조하는 것입니다.위의 결과를 보면 복사한 객체의 기본형 타입의 변수는 각각의 값을 가지게 되지만, 원본 객체와 복사한 객체가 가지고 있는 필드의 참조형 변수는 동일한 객체(MutableObject)를 참조하고 있습니다. 즉, 원본 객체와 복사한 객체가 가지고 있는 참조형 변수의 주소 값은 동일합니다.
기본형 타입의 변수의 값은 달라질 수 있지만, 참조형 변수가 참조하는 객체는 동일합니다.
얕은 복사는, 복사한 객체의 필드로 가지고 있는 참조형 변수의 주소값이 원본 객체의 필드에 참조형 변수와 주소 값이 동일하다.
✅깊은 복사(DeepCopy)
이번엔 깊은 복사에 대해서 알아보겠습니다.
- DeepCopy
package com.jhcode.copyobjects; public class DeepCopyClass implements Cloneable{ private int value; private MutableObject mutableObject; public DeepCopyClass(int value, MutableObject mutableObject) { this.value = value; this.mutableObject = mutableObject; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } public MutableObject getMutableObject() { return this.mutableObject; } @Override protected Object clone() throws CloneNotSupportedException { DeepCopyClass cloned = (DeepCopyClass) super.clone(); cloned.mutableObject = (MutableObject) mutableObject.clone(); // 필요한 경우 내부 객체도 복사 return cloned; } }
DeepCopy 클래스는 Cloneable 인터페이스를 구현하고, Object.clone()
메소드를 재정의해서 복사를 구현할 수 있습니다. clone()
메소드를 재정의할 때 원본 객체가 가지고 있는 참조형 변수인 MutableObject를 다시 clone()
하여 필드로 저장하는 것을 볼 수 있습니다. MutableObject 또한 clone()
메소드를 사용하기 위해서 Cloneable 인터페이스를 구현하고 clone()
메소드를 오버라이드했습니다.
public class MutableObject implements Cloneable {
//...생략
@Override //자동으로 Object 클래스를 상속함.
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
즉, 깊은 복사란 원본 객체가 필드로 참조하는 객체를 다시 복사하여, 새로운 객체를 생성하여 필드로 저장하는 것을 말합니다.
위의 결과와 같이 복사는 새로운 객체가 생성되는 것입니다. deepCopy와 deepCopyClone의 id 값이 다른 것을 확인할 수 있습니다. DeepCopy는 각각의 객체의 필드 또한 각각의 값을 가집니다. deepCopy의 Value 값은 50, deepCopyClone의 Value 값은 100으로 변경된 것을 확인할 수 있습니다.
가장 중요한 것은 deepCopy 객체의 필드로 저장하고 있는 mutableObject 객체가 새롭게 생성되어 deepCopyClone 객체에 저장된다는 것입니다. 즉, 필드에 참조하고 있는 객체도 복사하여 새롭게 생성됩니다.
기본형 타입의 변수의 값도 다르고, 참조형 변수가 참조하는 객체도 다르게 됩니다.
깊은 복사는, 복사한 객체의 필드로 가지고 있는 참조형 변수의 주소값이 원본 객체의 필드에 참조형 변수와 주소 값이 다르다. 즉, 새로운 객체를 생성해서 참조한다.
👉 Java API와 아래 외국계 사이트를 참고하시면 이해에 도움이 될 것 같습니다.(영어는 저도 번역기씁니다!ㅎㅎ 파파고 최고…) Java에서의 복사에 대해 바른 글들이 널리 퍼지길 바랍니다.
🏷️이미지 출처와 참고한 사이트
Uploaded by N2T