Cloneable 在 Java 中如何工作,我如何使用它?

我想知道以下情况:

  1. Cloneable意味着我们可以通过 实现 Cloneable接口 这么做的缺点是什么?
  2. 如果对象是一个? 递归克隆如何发生 合成物体?
81252 次浏览

The first thing you should know about Cloneable is - don't use it.

It is very hard to implement cloning with Cloneable right, and the effort is not worth it.

Instead of that use some other options, like apache-commons SerializationUtils (deep-clone) or BeanUtils (shallow-clone), or simply use a copy-constructor.

See here for the views of Josh Bloch about cloning with Cloneable, which explains the many drawbacks of the approach. (Joshua Bloch was a Sun employee, and led the development of numerous Java features.)

  1. Cloning invokes an extra-linguistic way of constructing objects - without constructors.
  2. Cloning requires you to treat somehow with CloneNotSupportedException - or to bother client code for treating it.
  3. Benefits are small - you just don't have to manually write a copying constructor.

So, use Cloneable judiciously. It doesn't give you sufficient benefits in comparison with the effort you need to apply to do everything right.

A) There are not a whole lot of advantages of clone over a copy constructor. Probably the biggest one is the ability to create a new object of the exact same dynamic type (assuming the declared type is clonable and has a public clone method).

B) The default clone creates a shallow copy, and it will remain a shallow copy unless your clone implementation changes that. This can be difficult, especially if your class has final fields

Bozho is right, clone can be difficult to get right. A copy constructor/factory will serve most needs.

Cloneable itself is unfortunately just a marker-interface, that is: it does not define the clone() method.

What is does, is change the behavior of the protected Object.clone() method, which will throw a CloneNotSupportedException for classes that do not implement Cloneable, and perform a member-wise shallow copy for classes that do.

Even if this is the behavior you are looking for, you'll still need to implement your own clone() method in order to make it public.

When implementing your own clone(), the idea is to start with the object create by super.clone(), which is guaranteed to be of the correct class, and then do any additional population of fields in case a shallow copy is not what you want. Calling a constructor from clone() would be problematic as this would break inheritance in case a subclass wants to add its own additional cloneable logic; if it were to call super.clone() it would get an object of the wrong class in this case.

This approach bypasses any logic that may be defined in your constructors though, which could potentially be problematic.

Another problem is that any subclasses that forget to override clone() will automatically inherit the default shallow copy, which is likely not what you want in case of mutable state (which will now be shared between the source and the copy).

Most developers don't use Cloneable for these reasons, and simply implement a copy constructor instead.

For more information and potential pitfalls of Cloneable, I highly recommend the book Effective Java by Joshua Bloch

Cloning is a basic programming paradigm. The fact that Java may have implemented it poorly in many ways does not at all diminish the need for cloning. And, it is easy to implement cloning that will work however you want it to work, shallow, deep, mixed, whatever. You can even use the name clone for the function and not implement Cloneable if you like.

Suppose I have classes A, B, and C, where B and C are derived from A. If I have a list of objects of type A like this:

ArrayList<A> list1;

Now, that list can contains objects of type A, B, or C. You don't know what type the objects are. So, you can't copy the list like this:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(new A(a));
}

If the object is actually of type B or C, you will not get the right copy. And, what if A is abstract? Now, some people have suggested this:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
if(a instanceof A) {
list2.add(new A(a));
} else if(a instanceof B) {
list2.add(new B(a));
} else if(a instanceof C) {
list2.add(new C(a));
}
}

This is a very, very bad idea. What if you add a new derived type? What if B or C are in another package and you don't have access to them in this class?

What you would like to do is this:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(a.clone());
}

Lots of people have indicated why the basic Java implementation of clone is problematic. But, it's easily overcome this way:

In class A:

public A clone() {
return new A(this);
}

In class B:

@Override
public B clone() {
return new B(this);
}

In class C:

@Override
public C clone() {
return new C(this):
}

I'm not implementing Cloneable, just using the same function name. If you don't like that, name it something else.

What are disadvantages of Cloneable?

Cloning is very dangerous if the object whom you are copying has composition.You need to think about below possible side effect in this case because clone creates shallow copy:

Let say you have one object to handle db related manipulation. Say, that object has Connection object as one of the property.

So when someone creates clone of originalObject, The object being created, let say, cloneObject. Here the originalObject and cloneObject hold the same reference for Connection object.

Let say originalObject closes the Connection object, so now the cloneObject will not work because the connection object was shared between them and it was actaually closed by the originalObject.

Similar problem may occur if let say you want to clone a object which has IOStream as a property.

How does the recursive cloning happen if the object is a composite object?

Cloneable performs shallow copy. Meaning is that data of original object and clone object will point to the same reference/memory. contrary in the case of deep copy, data from memory of original object is copied to the memory of clone object.