2

我正在学习对象深度克隆,我有一个带有 getInstance 方法的员工类,它返回一个单例,我正在克隆返回的对象。下面是类和测试类。

public class Employee  implements Serializable , Cloneable {

    public static Employee employee;

    private String name;

    private int age;


    private Employee(){


    }


    public Employee(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    protected Object clone() throws CloneNotSupportedException {

        return super.clone();

        }


    public static Employee getInstance(){

        if(employee == null ){
            employee = new Employee();
            return employee;
        }


        return employee;
    }


    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }


    public int getAge() {
        return age;
    }


    public void setAge(int age) {
        this.age = age;
    }



}

对象深拷贝测试类

public class CopyTest {

    /**
     * @param args
     */
    public static void main(String[] args) {



        try {

            Employee original = Employee.getInstance();

            original.setName("John");
            original.setAge(25);

            Employee cloned = (Employee)copy(original);

            System.out.println("Original -->"+Employee.getInstance().getName());

            cloned.getInstance().setName("Mark");

            System.out.println("Cloned -->"+cloned.getInstance().getName());

            System.out.println("Original -->"+Employee.getInstance().getName());

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }


    public static Object copy(Object orig) {
        Object obj = null;
        try {
            // Write the object out to a byte array
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bos);
            out.writeObject(orig);
            out.flush();
            out.close();

            // Make an input stream from the byte array and read
            // a copy of the object back in.
            ObjectInputStream in = new ObjectInputStream(
                new ByteArrayInputStream(bos.toByteArray()));
            obj = in.readObject();
        }
        catch(IOException e) {
            e.printStackTrace();
        }
        catch(ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
        return obj;
    }
}

输出

Original -->John
Cloned -->Mark
Original -->Mark

问题

尽管我克隆了原始对象以创建它的副本,

Employee cloned = (Employee)copy(original);

我通过调用修改克隆对象的属性

cloned.getInstance().setName("Mark");

正如您从控制台输出中看到的那样,它反映到原始对象。我认为这是因为静态调用?,我该如何克服呢?我是否违反了通过 getInstance 方法需要对象的单个实例的原则,然后我决定稍后制作该对象的副本。

4

4 回答 4

1

您正在字段Employee实例上设置名称staticemployee

cloned.getInstance().setName("Mark");
      ^ static method call that returns the employee reference

并打印它

System.out.println("Cloned -->"+cloned.getInstance().getName());
                                      ^ static method call

您可能想要实际更改cloned实例

cloned.setName("Mark");
System.out.println("Cloned -->"+cloned.getName());
于 2013-10-15T19:27:31.093 回答
1

静态字段大致意味着它将被每个对象共享。无论您创建/克隆了多少对象,您的 getInstance() 调用都将返回相同的 Employee。

因此,只要您将其名称设置为 Mark,您将始终在控制台中获得 Mark。

例如:

Employee me = new Employee();
Employee.getInstance().setName("Mark");
System.out.println("me employee name? " + me.getInstance().getName());

如果您在控制台中获得“Mark”,请不要感到惊讶;)

有关实例和类(使用 static 关键字)成员之间区别的更详细信息,请查看本教程部分

于 2013-10-15T19:32:49.310 回答
1

这是JAVA Doc教程页面所说的:

声明中带有 static 修饰符的字段称为静态字段或类变量。它们与类相关联,而不是与任何对象相关联。类的每个实例共享一个类变量,该变量位于内存中的一个固定位置。任何对象都可以更改类变量的值,但也可以在不创建类实例的情况下操作类变量。

克隆的对象cloned只是该类的另一个对象。它不会更改它没有引用的另一个对象。您的调用cloned.getInstance()将返回employee所有对象都可以访问的静态对象,因为静态对象与 关联Class,而不是任何特定对象。所以你打电话给cloned.getInstance().setName("Mark");就相当于Employee.employee.setName("Mark");

于 2013-10-15T19:48:20.390 回答
0

您将要序列化对象,然后反序列化它。在java中,反序列化保证创建一个新对象。(与 Classloader 的工作方式有关)。

当您序列化对象时,它会对对象及其所有依赖项等进行深度复制。序列化可以通过多种方式使用,但一种方法是将对象转换为字节,以便可以通过线路发送(网络),或存储在磁盘上以保持持久性。您可以使用该行为进行内存中的序列化/反序列化,并每次使用完全相同的内容(值)创建一个新对象,但引用与原始对象不同。

这是我在某些应用程序中进行生产的一种方法:

/**
 * Clones an object creating a brand new
 * object by value of input object. Accomplishes this
 * by serializing the object, then deservializing it.
 * 
 * @param obj Input Object to clone
 * @return a new List<Product> type cloned from original.
 * @throws IOException If IOException
 * @throws ClassNotFoundException If ClassNotFoundException
 */
private static List<Product> cloneProdList(Object obj) throws IOException, ClassNotFoundException {

    java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
    java.io.ObjectOutputStream obj_out = new java.io.ObjectOutputStream(bos);
    obj_out.writeObject(obj);

    java.io.ByteArrayInputStream bis = new java.io.ByteArrayInputStream(bos.toByteArray());
    java.io.ObjectInputStream obj_in = new java.io.ObjectInputStream(bis);

    @SuppressWarnings("unchecked")
    List<Product> newObj = (List<Product>)obj_in.readObject();

    bos.close();
    bis.close();
    obj_out.close();
    obj_in.close();

    return newObj;
}
于 2013-10-15T19:27:46.050 回答