关于java:对象是克隆的,但静态引用仍然存在?

Object is cloned , but static references still exists?

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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;
    }



}

对象深度复制测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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;
    }
}

产量

1
2
3
Original -->John
Cloned -->Mark
Original -->Mark

问题

即使我克隆原始对象来创建它的副本,

1
Employee cloned = (Employee)copy(original);

我通过调用

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

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


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

因此,一旦您将其名称设置为mark,您将始终在控制台中获得mark。

例如:

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

如果控制台中出现"mark",不要感到惊讶;)

有关实例和类(使用static关键字)成员之间差异的详细信息,请参阅本教程部分


您正在设置static字段EmployeeEmployee实例的名称。

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

也打印出来了

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

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

1
2
cloned.setName("Mark");
System.out.println("Cloned -->"+cloned.getName());


这就是Java doc教程页面所说的:

Fields that have the static modifier in their declaration are called
static fields or class variables. They are associated with the class,
rather than with any object. Every instance of the class shares a
class variable, which is in one fixed location in memory. Any object
can change the value of a class variable, but class variables can also
be manipulated without creating an instance of the class.

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


您将要序列化对象,然后反序列化它。在爪哇,反序列化保证创建一个新对象。(与类加载器的工作方式有关)。

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

以下是我在某些应用程序的生产中使用的一种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * 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;
}