首页 文章

克隆了对象,但静态引用仍然存在?

提问于
浏览
0

我正在学习对象深度克隆,我有一个带有getInstance方法的employee类,它返回一个单例,我正在克隆返回的对象.Below是类和测试类 .

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



}

Object deep copy test class

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");

从控制台输出中可以看到它反映到原始对象 . I assume this is because of the static call ? , and how do I overcome this ? 我违反了通过getInstance方法需要对象的单个实例的原则,然后我决定稍后复制该对象 .

4 回答

  • 0

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

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

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

  • 1

    您正在 static 字段的 Employee 实例上设置名称 employee

    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());
    
  • 0

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

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

    例如:

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

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

    有关实例和类(使用static关键字)成员之间差异的更详细信息,请查看此tutorial section

  • 1

    您将要序列化对象,然后反序列化它 . 在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;
    }
    

相关问题