얕은 복사는 주소값을 복사하는 것이고, 깊은 복사는 실제값을 새로운 메모리 공간에 복사하는 것이다.
이 둘의 차이가 무엇일까?
얕은 복사는 같은 주소를 사용하기 때문에
A의 값이 변경되면 A와 같은 주소를 가지고 있는 다른 객체도
같이 값이 변경된다는 것이다.
글로는 어려우니 예제를 통해 이해해 보도록 하자.
public class Human {
String name;
int age;
public Human(String name, int age) {
this.name = name;
this.age = age;
}
public Human() {}
public void changeName(String name) {
this.name = name;
}
public void changeAge(int age) {
this.age = age;
}
}
Human이라는 클래스를 하나 준비해준다.
728x90
1. 얕은 복사(Shallow Copy)
class Main {
public static void main(String[] args) {
shallowCopy();
}
public static void shallowCopy() {
Human human = new Human("sooyeol", 26);
Human humanCopy = human;
human.changeName("minsoo");
human.changeAge(30);
System.out.println(human.age);
System.out.println(human.name);
System.out.println(humanCopy.age);
System.out.println(humanCopy.name);
}
}
둘 중 한명이 값이 바뀐다면, heap에 있는 해당 주소 값에 데이터가 바뀌는 것이기 때문에
두 값 모두 값이 바뀌는 것입니다.
하지만 이런식으로 복사하는 경우도 있지만,
보통은 실제값을 복사해야 할 것입니다.
저렇게 같은 주소값을 공유하는 것이 아닌 복사를 통해
독립된 인스턴스를가진 상태 말입니다.
그러려면 우리는 깊은 복사를 해야 합니다.
2. 깊은 복사(Deep Copy)
깊은 복사를 하는 방법은 여러가지 있습니다.
1. 복사 생성자 혹은 복사 팩토리를 이용해서 복사합니다.
2. 직접 객체 생성을 하여 복사합니다.
3. Cloneable을 구현하여 clone() 메서드로 재정의
(clone() 재정의는 final 인스턴스나 배열이 아닌 경우 사용을 권하지 않는 다고 합니다.)
2-1 복사생성자, 복사 팩토리
우선 Human클래스의 변화를 주겠다.
public class Human {
String name;
int age;
//복사 생성자
public Human(Human human) {
this.name = human.name;
this.age = human.age;
}
public Human() {
this("sooyeol", 26);
}
public Human(String name, int age) {
this.name = name;
this.age = age;
}
//복사 팩토리
public static Human newInstance(Human human) {
Human h = new Human();
h.name = human.name;
h.age = human.age;
return h;
}
}
복사 생성자와 복사 팩토리를 각각 만들어 주었다.
복사 생성자는 다음과 같이 사용한다.
Human 모델 클래스에 this는 안에 포함된 파라미터에 맞는
생성자를 호출해줍니다.
ex)
public Human() {
this("sooyeol", 26);
}
=> public Human(String name, int age) {
this.name = name;
this.age = age;
}
//이것과 같다.
복사 생성자와 복사 팩토리에 사용법은 다음과 같다.
public static void main(String[] args) {
//복사 생성자
Human human = new Human();
Human human2 = new Human(human);
human2.age = 30;
human2.name = "minsoo";
System.out.println(human.age); //26
System.out.println(human2.age); //30
System.out.println(human.name); //sooyeol
System.out.println(human2.name); //minsoo
//복사 팩토리
Human human3 = new Human();
Human human4 = Human.newInstance(human3);
human4.age = 30;
human4.name = "minsoo";
System.out.println(human3.age); //26
System.out.println(human4.age); //30
System.out.println(human3.name); //sooyeol
System.out.println(human4.name); //minsoo
}
두 가지 방법 모두 다른 주소 값을 가지게 만들어 주는 방법을 사용하여,
한쪽에서 값을 바꾸어도, 기존에 복사받아왔던 값은 원래 값을 가지고 있다.
반응형
2-2 직접 객체를 생성하여 복사
class Human {
String name;
int age;
public Human() {
this("sooyeol", 26);
}
public Human(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Main {
public static void main(String[] args) {
deepCopy();
}
static void deepCopy() {
Human human = new Human("minsoo", 30);
Human humanCopy = new Human();
humanCopy.setName(human.getName());
humanCopy.setAge(human.getAge());
humanCopy.name = "sooyeol";
humanCopy.age = 26;
System.out.println(human.getName()); //minsoo
System.out.println(human.getAge()); //30
System.out.println(humanCopy.getName()); //sooyeol
System.out.println(humanCopy.getAge()); //26
}
}
객체를 하나 생성하고, 해당 인스턴스를 파라미터로 넘겨주는 복사 방법이다.
이 역시 humanCopy를 다른 값으로 바꾸어주니,
둘 다 다른 인스턴스라는 것을 프린트로 찍어보면 알 수가 있다.
2-3 Cloneable을 구현하여 clone()메서드로 재정의 (비추천)
class Human implements Cloneable {
String name;
int age;
@NonNull
@Override
protected Human clone() throws CloneNotSupportedException {
return (Human) super.clone();
}
public Human() {
this("sooyeol", 26);
}
public Human(String name, int age) {
this.name = name;
this.age = age;
}
}
class Main {
public static void main(String[] args) throws CloneNotSupportedException {
deepCopy();
}
private static void deepCopy() throws CloneNotSupportedException {
Human human = new Human("sooyeol", 26);
Human humanCopy = human.clone();
humanCopy.name = "minsoo";
humanCopy.age = 30;
System.out.println(human.name);
System.out.println(human.age);
System.out.println(humanCopy.name);
System.out.println(humanCopy.age);
}
}