简体   繁体   English

Java深度克隆问题

[英]Java deep clone issue

I am learning Java about object clone. 我正在学习有关对象克隆的Java。 I am really confused by shallow clone and deep clone. 浅克隆和深克隆我真的很困惑。 Below is a sample code from Core Java 以下是来自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: 但是当我将方法setHireDay更改为此时:

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 我确实更改了克隆对象中的字段hiredDay的值,但它不会影响原始对象。我不知道为什么

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). 在更改的setHireDay方法中,“ hireDay”变量指向另一个内存位置,而在第一个setHireDay方法中,内存位置未更改(但其引用的值已更改)。 It helps to think of Object variables like "hireDay" as primitive long values that specify a memory address (aka pointers). 它有助于将“ hireDay”之类的对象变量视为指定内存地址(即指针)的原始长值。 At this memory address the actual Object data is stored (like the time-value of the "hireDay" variable). 实际的对象数据存储在该存储器地址中(例如“ hireDay”变量的时间值)。 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. hireDay = new Date()的情况下,内存地址被更改,而在hireDay.setTime(x)的情况下,内存地址保持不变,但是所引用的值被更改。

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. 下面对此进行了演示(将其作为Java应用程序运行并将输出与源代码进行比较),以及有关如何进行深层复制与浅层复制的示例。

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;
}

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM