객체 복제 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;
}
}
'자바' 카테고리의 다른 글
JAVA 자바 Objects 클래스 객체 비교 compare(T t1, T t2, Comparator<T> c) /// 동등비교 equals() 와 deepEquals() (0) | 2017.03.14 |
---|---|
JAVA 자바 Object 클래스 객체 소멸자 finalize() (0) | 2017.03.14 |
JAVA 자바 Object 클래스 객체의 문자 정보 toString() (0) | 2017.03.14 |
JAVA 자바 Object 클래스 객체 해시코드 hashCode() (0) | 2017.03.14 |
JAVA Object 클래스 객체 비교 equals() (0) | 2017.03.14 |