자바

JAVA 자바 Object 클래스 객체 복제 clone

알통몬_ 2017. 3. 14. 10:13
반응형

객체 복제 clone()

 객체 복제 : 원본 객체의 필드값과 동일한 값을 가지는 새로운 객체를 생성하는 것

객체를 복제하는 이유 : 원본 객체를 안전하게 보호하기 위해

신뢰하지 않은 영역으로 원본 객체를 넘겨 작업할 경우 

원본 객체의 데이터가 훼손될 수 있기 때문에 복제된 객체를 만들어 신뢰하지 않는 영역으로 넘기는 것이 좋습니다. 

객체를 복제하는 방법은 얕은 복제와 깊은 복제가 있습니다.


얕은 복제(thin clone)

 필드 값을 복사해서 객체를 복제하는 것. 필드값만 복제하기 때문에 

필드가 기본 타입일 경우 값 복사가 일어나고 필드가 참조 타입일 경우에는 객체의 번지가 복사됩니다. 

예)원래 객체에 int 타입의 필드와 배열 타입의 필드가 있을 경우 얕은 복제된 필드의 값은 아래 그림과 같습니다.

 

clone() 메서드는 자신과 동일한 필드값을 가진 얕은 복제된 객체를 리턴합니다. 

clone() 메서드로 객체를 복제하려면 반드시 원본 객체는 Cloneable 인터페이스를 구현 해야함. 

메서드 선언이 없음에도 Cloneable 인터페이스를 명시적으로 구현하는 이유 :

클래스의 설계자가 복제를 허용한다는 의도적 표현을 하기 위해. 

설계자가 복제를 허용하지 않는다면 Cloneable 인터페이스를 구현하지 않으면 됩니다. 

Cloneable 인터페이스를 구현하지 않는다면 clone() 메서드를 호출할 때 CloneNotSupportedException 예외가 발생. 

clone()은 CloneNotSupportedException 예외처리가 필요한 메소드라서 try - catch 문이 필요합니다.

try{

   Object o = clone();

}catch(CloneNotSupportedException e) { }


예제

원본 클래스

public class Member implements Cloneable { // Cloneable 인터페이스를 구현했으니 

public String id;                                           복제할 수 있다는 뜻이겠죠?

public String name;

public String password;

public int age;

public boolean adult;

public Member(String id,  String name, String password, int age, boolean adult ) {

this.id = id;

this.name = name;

this.password = password;

this.age = age;

this.adult = adult;

}

public Member getMember() {

Member cloned = null;

try {

cloned = (Member) clone(); //clone() 메서드의 리턴타입이 Object이므로 

                                                    Member 타입으로 캐스팅해야 합니다.

} catch (CloneNotSupportedException e) {

e.printStackTrace();

}

return cloned;

}

 

}


실행 클래스

public class MemberExample {

public static void main(String[] args) {

//원본 객체 생성

Member original = new Member(" yellow", "부르곰", "12345", 25, true);

//복제 객체를 얻은 후에 패스워드 변경

Member cloned = original.getMember(); // 복제 객체

cloned.password = "67890"; // 복제 객체의 비밀번호 변경

System.out.println("[복제 객체의 필드값]");

System.out.println("id: " + cloned.id);

System.out.println("name: " + cloned.name);

System.out.println("password: " + cloned.password);

System.out.println("age: " + cloned.age);

System.out.println("adult: " + cloned.adult);

System.out.println();

System.out.println("[원본 객체의 필드값]");

System.out.println("id: " + original.id);

System.out.println("name: " + original.name);

System.out.println("password: " + original.password);// 하지만 원본 객체의 비밀번호                System.out.println("age: " + original.age);                  는 변함이 없죠~

System.out.println("adult: " + original.adult);

}

 

}


깊은 복제 deep clone

얕은 복제의 단점 : 복제 객체에서 참조 객체를 변경하려면 원본 객체도 같이 변경됩니다. 

깊은 복제는 참조하고 있는 객체도 복제하는 것을 말합니다. 


깊은 복제를 하려면 Object 의 clone() 메서드를 오버라이딩해서 

참조 객체를 복제하는 코드를 직접 작성해야 해요.

import java.util.Arrays;


public class Member implements Cloneable {

public String name;

public int age;

public int[] scores; // 참조 타입 필드

public Car car;     // 깊은 복제 대상

public Member(String name, int age, int[] scores, Car car) {

this.name = name;

this.age = age;

this.scores = scores;

this.car = car;

}

@Override // clone() 메서드 재정의

protected Object clone() throws CloneNotSupportedException {

//먼저 얕은 복사를 해서 name, age를 복제한다.

Member cloned = (Member) super.clone();Object의 clone() 호출

//scores를 복제한다.

cloned.scores = Arrays.copyOf(this.scores, this.scores.length);

//car를 복제한다.

cloned.car = new Car(this.car.model);

//깊은 복제된 Member 객체를 리턴

return cloned;

}

public Member getMember() {

Member cloned = null;

try {

cloned = (Member) clone(); // 재정의된 clnoe() 메서드 호출

} catch (CloneNotSupportedException e) {

e.printStackTrace();

}

return cloned;

}

 

}


public class Car {
public String model;
public Car(String model) {
this.model = model;
}
}

아래 예제는 Member 클래스의 getMemeber() 메서드를 호출해서 
복제된 Member 객체를 얻은 후에 scores 배열 항목값과 car 객체의 모델을 변경합니다. 
하지만 원본 Member 객체의 scores 배열 항목값과 car 객체의 모델은 변함이 없는 볼 수 있습니다.

public class MemberExample {
public static void main(String[] args) {
//원본 객체 생성
Member original = new Member("홍길동", 25, new int[] {90, 90}, new Car("소나타"));
//복제 객체를 얻은 후에 참조 객체의 값을 변경
Member cloned = original.getMember();
cloned.scores[0] = 100;
cloned.car.model = "그랜저";
System.out.println("[복제 객체의 필드값]");
System.out.println("name: " + cloned.name);
System.out.println("age: " + cloned.age);
System.out.print("scores: {");
for(int i=0; i<cloned.scores.length; i++) {
System.out.print(cloned.scores[i]);
System.out.print((i==(cloned.scores.length-1))?"":",");
}
System.out.println("}");
System.out.println("car: " + cloned.car.model);
System.out.println();
System.out.println("[원본 객체의 필드값]");
System.out.println("name: " + original.name);
System.out.println("age: " + original.age);
System.out.print("scores: {");
for(int i=0; i<original.scores.length; i++) {
System.out.print(original.scores[i]);
System.out.print((i==(original.scores.length-1))?"":",");
}
System.out.println("}");
System.out.println("car: " + original.car.model);
}
}
 실행은 각자 해보시면 되겠습니다~~


반응형