【问题标题】:Object is cloned , but static references still exists?对象被克隆,但静态引用仍然存在?
【发布时间】:2013-10-23 17:43:20
【问题描述】:

我正在学习对象深度克隆,我有一个带有 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 方法创建对象的单个实例的原则,然后我决定稍后制作该对象的副本。

【问题讨论】:

    标签: java object clone static-methods


    【解决方案1】:

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

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

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

    【讨论】:

    • "类的每个实例共享一个类变量,该变量位于内存中的一个固定位置。"让我明白了。接受答案。
    【解决方案2】:

    您正在为static 字段employeeEmployee 实例设置名称

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

    【讨论】:

    • 我是故意这样做的,所以这意味着 getInstance() 将始终引用原始对象,即使它是克隆的?
    • @TitoCheriachan getInstance() 方法是 static。它与实例无关。您只是碰巧在对象而不是类型上调用它。在这种情况下,cloned.getInstance() 等同于 Employee.getInstance()
    • 所以这意味着如果一个克隆对象有一个静态引用,它可以恶作剧地修改原始对象。所以根据经验,如果我想克隆对象,我不应该公开其类的静态方法?
    • @TitoCheriachan 这与克隆对象无关。它与您在 static 字段引用的对象上调用 setget 方法有关,而不是您的 cloned 引用。
    【解决方案3】:

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

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

    例如:

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

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

    有关实例和类(使用静态关键字)成员之间区别的更详细信息,请查看tutorial section

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • @TitoCheriachan clone() 方法很少奏效。您需要自己进行序列化,就像我在上面的示例中提供的那样。
      • 我没有使用 clone ,我使用的是自定义方法 copy(..) ,就像您的示例代码一样。员工克隆 = (Employee)copy(original);
      • @TitoCheriachan 哦,你知道吗,我忘了在你的例子中向下滚动!现在我看到你在哪里处理序列化。 ;-P
      • @TitoCheriachan 在这种情况下,如果你“克隆”一个静态对象,那么根据静态的定义,它将是相同的引用。 static 只能有 1 个同时存在于 JVM 中。
      • @SnakeDoc OP 正在克隆一个对象,而不是一个引用。对象将完全不同。但是,static 字段引用不会改变。
      猜你喜欢
      • 1970-01-01
      • 2022-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-26
      • 1970-01-01
      • 2021-03-06
      • 2022-11-10
      相关资源
      最近更新 更多