본문 바로가기

JAVA/JAVA 기초

Object의 clone() 복사

반응형

Object 클래스의 clone() 함수


implements의 Cloneable으로 구현하고

Object의 clone() 함수를 재정의 한다.


package clone;


public class MyValue implements Cloneable{

   private String name;

   private int age;



   public MyValue(String name, int age) {

       this.name = name;

       this.age = age;

   }


   public void setAge(int age) {

       this.age = age;

   }


   @Override

   public String toString() {

       return "MyValue{" +

               "name='" + name + '\'' +

               ", age=" + age +

               '}';

   }


   @Override

   protected MyValue clone() throws CloneNotSupportedException {

       return (MyValue) super.clone();

   }


   public static void main(String[] args) throws CloneNotSupportedException {

       MyValue value = new MyValue("Lee", 10);


       MyValue value1 = value.clone();


       System.out.println(value);

       value.name ="kim";

       value.age = 11;


       System.out.println(value);

       System.out.println(value1);


   }

}


결과

MyValue{name='Lee', age=10}

MyValue{name='kim', age=11}

MyValue{name='Lee', age=10}


MyValue의 멤버변수들은 String형, int형 기본형이다.

불변객체이다.


public final class String

   implements java.io.Serializable, Comparable<String>, CharSequence {


public final class Integer extends Number implements Comparable<Integer> {


따라서 값으로 복사가 된다.

원본의 value 객체의 값을 변경해도 value1에는 영향이 없다.


package clone;


import java.util.Arrays;


public class MyRefer implements Cloneable{

   private String id;

   private MyValue value;

   private MyCar car;

   private int size = 0;

   private Object[] elements;


   private static final int DEFAULT_INITIAL_CAPACITY = 10;


   public MyValue getValue() {

       return value;

   }


   public void setValue(MyValue value) {

       this.value = value;

   }


   public MyCar getCar() {

       return car;

   }


   public void setCar(MyCar car) {

       this.car = car;

   }


   public MyRefer(String id, MyValue value, MyCar myCar) {

       this.id = id;

       this.value = value;

       this.car = myCar;

       this.elements = new Object[DEFAULT_INITIAL_CAPACITY];

   }


   public void push(Object o) {

       elements[size++] = o;

   }


   @Override

   public String toString() {

       return "MyRefer{" +

               "id='" + id + '\'' +

               ", value=" + value +

               ", car=" + car +

               ", elements=" + Arrays.toString(elements) +

               '}';

   }


   @Override

   protected MyRefer clone() throws CloneNotSupportedException {

       MyRefer myRefer = (MyRefer) super.clone();

       myRefer.elements = elements.clone();    //가변까지 복사

       MyValue value = myRefer.getValue();     //MyValue 참조형까지 복사

       myRefer.value = value.clone();


       return myRefer;

       //return (MyRefer) super.clone();

   }


   public static void main(String[] args) throws CloneNotSupportedException {

       MyValue myValue = new MyValue("Lee", 10);

       MyCar myCar = new MyCar("1111","Park");

       MyRefer myRefer = new MyRefer("123", myValue, myCar);

       myRefer.push("Coffee");

       myRefer.push("phone");

       //myRefer.setCar(myCarcar);


       MyRefer myRefer1 = myRefer.clone();

       System.out.println("===복사 후====");

       System.out.println(myRefer);

       System.out.println(myRefer1);


       System.out.println("===복사후 원본 변경====");

       myRefer.id = "234";

       myRefer.setValue(new MyValue("Kim", 20));

       //myRefer.setCar(new MyCar("2222", "Kim"));

       myRefer.push("book");

       myCar.setOwner("John");

       myValue.setAge(21);


       System.out.println(myRefer);

       System.out.println(myRefer1);


   }

}


package clone;


public class MyCar {

    private String carNumber;

    private String owner;


    public void setOwner(String owner) {

        this.owner = owner;

    }


    public MyCar(String carNumber, String owner) {

        this.carNumber = carNumber;

        this.owner = owner;

    }


    @Override

    public String toString() {

        return "MyCar{" +

                "carNumber='" + carNumber + '\'' +

                ", owner='" + owner + '\'' +

                '}';

    }

}


결과

===복사 후====

MyRefer{id='123', value=MyValue{name='Lee', age=10}, car=MyCar{carNumber='1111', owner='Park'}, elements=[Coffee, phone, null, null, null, null, null, null, null, null]}

MyRefer{id='123', value=MyValue{name='Lee', age=10}, car=MyCar{carNumber='1111', owner='Park'}, elements=[Coffee, phone, null, null, null, null, null, null, null, null]}

===복사후 원본 변경====

MyRefer{id='234', value=MyValue{name='Kim', age=20}, car=MyCar{carNumber='1111', owner='John'}, elements=[Coffee, phone, book, null, null, null, null, null, null, null]}

MyRefer{id='123', value=MyValue{name='Lee', age=10}, car=MyCar{carNumber='1111', owner='John'}, elements=[Coffee, phone, null, null, null, null, null, null, null, null]}



MyRefer 클래스는


String형 멤버변수

MyValue 참조형 멤버변수

MyCar 참조형 멤버변수

Object[] 가변배열 멤버변수


clone() 함수를 재정의 할때

MyRefer 만 복사한다면

참조형이나 가변은 얇은 복사만 된다. 즉 참조 주소만 복사가 된다.


따라서 제대로 된 복사를 할 경우에는 그 참조형 멤버변수들도 복사를 해야한다.

MyValue 참조형 멤버변수 경우 MyValue 클래스가 Cloneable을 구현하고 있기에 clone()으로 복사가 가능하지만, MyCar 경우는 Clonable을 구현하고 있지 않기 때문에 복사가 불가능하다.


원본 myRefer 객체에 MyValue값은 new로 새로운 MyValue값을 교체했다.

하지만 myCar의 경우는 참조형 myCar의 setOwner값을 변경하였더니 복사한 myRefer1 객체의 myCar의 owner 값도 같이 변경되었다.

즉 참조 주소값만 복사가 된 것이다.


elements의 경우는 clone()에서 myRefer.elements = elements.clone(); 가변까지 복사했으므로 영향을 받지 않는다.


따라서 clone()으로 복사할 경우 참조형이나 가변형 경우도 제대로 복사가 되는지 주의해야 한다.


개발시 clone()으로 복사했지만, 그 참조형의 값을 누군가가 변경한다면 복사한 객체의 내부도 변경될 가능성이 있기 때문이다.






반응형

'JAVA > JAVA 기초' 카테고리의 다른 글

자바 코딩의 기술 - #1 코드 정리  (0) 2020.11.26
자바 코딩 규약  (0) 2019.11.26
객체의 라이프 사이클  (0) 2018.10.25
가시성  (0) 2018.10.25
값 전달 방법(callByValue callByReference)과 불변객체  (0) 2018.10.25