Pergunta

I am learning Java about object clone. I am really confused by shallow clone and deep clone. Below is a sample code from Core Java

public class Employee implements Cloneable {
    private String name;
    private Date hireDay;

    public Employee(String n,double s)
    {
        this.name=n;
        hireDay=new Date();
    }
    public void setHireDay(int year,int month,int day)
    {
        Date newhireDay=new GregorianCalendar(year,month-1,day).getTime();
        hireDay.setTime(newhireDay.getTime());
    }
    public Employee clone() throws CloneNotSupportedException
    {
      Employee cloned=(Employee) super.clone();
    //clone mutable fields
    //cloned.hireDay=(Date) hireDay.clone();
      return cloned;
    }
    public String toString()
    {
    return "Employee[name=]"+ name+" salary= "+salary+" hireday.getTIme() "+hireDay+"]";
    }
}
public class cloneTest {
    public static void main(String[] args) {
    try{
        Employee original =new Employee("john");
        Employee cloned=original.clone();
        original.setHireDay(1993,2,22);
        cloned.setHireDay(2000,11,22);
        System.out.println("original="+original);
        System.out.println("cloned= "+cloned);
    }
    catch(CloneNotSupportedException e){
        e.printStackTrace();
    }
}

}  

In this case, output of original object and cloned object are the same.Since I didn't clone the mutable field,the change made on cloned objects will affects original object. But when I change the method setHireDay into this:

public void setHireDay(int year,int month,int day)
    {
    hireDay=new GregorianCalendar(year,month-1,day).getTime();
 }

I did change the value of field hireDay in cloned object but it doesn't affect original object.I don't know why

Foi útil?

Solução

In the changed setHireDay method the "hireDay" variable is pointing to another memory location while in the first setHireDay method the memory location is unchanged (but the value it refers to is changed). It helps to think of Object variables like "hireDay" as primitive long values that specify a memory address (a.k.a. pointers). At this memory address the actual Object data is stored (like the time-value of the "hireDay" variable). In the case of hireDay = new Date() the memory address is changed, while in the case of hireDay.setTime(x) the memory address stays the same but the value referred to is changed.

Below a demonstration of this (run it as a Java application and compare the output with the source code) plus an example of how to make a deep copy versus a shallow copy.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.GregorianCalendar;

public class CloneTest implements Cloneable, Serializable {
private static final long serialVersionUID = 5971741470680723802L;

public static void main(String[] args) {

    try {
        CloneTest original = new CloneTest();

        System.out.println();
        System.out.println("### shallow clone");

        CloneTest cloned=original.clone();
        compare(original, cloned);
        original.setHireDay(1993,2,22);
        cloned.setHireDay(2000,11,22);
        compare(original, cloned);

        System.out.println();
        System.out.println("### shallow clone - mutable hiredate");

        cloned.hireDayMutable = true;
        cloned.setHireDay(2002,11,22);
        compare(original, cloned);

        System.out.println();
        System.out.println("### deep clone");

        cloned = clone(original);
        compare(original, cloned);
        cloned.setHireDay(2004,11,22);
        compare(original, cloned);

    } catch(Exception e){
        e.printStackTrace();
    }
}

private Date hireDay;
public boolean hireDayMutable;

public CloneTest() {
    super();
    hireDay=new Date();
    System.out.println("New instance");
}

public void setHireDay(int year, int month, int day) {

    if (hireDayMutable) {
        hireDay = new GregorianCalendar(year,month-1,day).getTime();
    } else {
        Date newhireDay = new GregorianCalendar(year,month-1,day).getTime();
        hireDay.setTime(newhireDay.getTime());
    }
}

public CloneTest clone() throws CloneNotSupportedException {

    CloneTest cloned = (CloneTest)super.clone();
    return cloned;
}

public String toString() {
    return "CloneTest[hireday=" + hireDay + "]";
}

public static void compare(CloneTest original, CloneTest cloned) {

    System.out.println();
    System.out.println("The same object  : " + (cloned == original));
    System.out.println("The same hireDate: " + (cloned.hireDay == original.hireDay));
    System.out.println("original = " + original);
    System.out.println("cloned   = " + cloned);
}

/**
 * Clones an object by serializing and then unserializing it ("deep copy").
 */
@SuppressWarnings("hiding")
public static <T> T clone(T o) {
    return clone(o, 512);
}

@SuppressWarnings({ "unchecked", "hiding" })
public static <T> T clone(T o, int bufSize) {
    return (T) unserialize(serialize(o, bufSize));
}

public static byte[] serialize(Object o, int bufSize) {

    ByteArrayOutputStream baos = new ByteArrayOutputStream(bufSize);
    try {
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject((Serializable)o);
        oos.close();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    byte[] ba = baos.toByteArray();
    // log.trace("Serialized size: {}", ba.length);
    return ba;
}

public static Object unserialize(byte[] ba) {

    Object o = null;
    try {
        ByteArrayInputStream bais = new ByteArrayInputStream(ba);
        ObjectInputStream oin = new ObjectInputStream(bais);
        o = oin.readObject();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return o;
}

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top