想要一只一样的羊,那就克隆羊。
想要一个一样的Object,那就克隆Object。
Object提供了一个名为clone()的方法,可以实现现有对象的克隆,也就是拷贝,默认采用本地方法的方式实现,如果子类不重写clone()方法,则必须实现标记接口Cloneable,否则调用本地方法的clone()会抛异常。
一、Object的clone()
public class Object {
...
/**
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
...
}
为便于观察,只抽出了Object类中的clone()方法以及关键注释。
可以看到:
1.此方法被native
关键字修饰,没有方法体,具体的实现通过调用非java的实现而实现。
2.访问修饰符为protected
,可以由子类继承调用。
3.可能抛出CloneNotSupportedException
异常。
4.注释表明每一个想要实现克隆的Object都需要实现Cloneable
接口,此接口只起到标记作用,表明此Object可以被克隆,否则调用clone方法时会抛出CloneNotSupportedException
异常。
5.返回值类型为Object。
二、简单实现一个克隆操作
定义如下Tiger类:
@Data
@AllArgsConstructor
public class Tiger implements Cloneable{
private String name;
private Integer age;
private Address address;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
@Data
@AllArgsConstructor
class Address {
private String info;
}
测试类使用了Lombok插件。
1.Tiger类实现了Cloneable接口,表示可以进行克隆操作,避免抛出CloneNotSupportedException
异常。
2.定义了三个成员变量。
3.重写了Object的clone()方法,并调用了Object中的clone()方法,所以就是调用的本地方法来实现对Tiger的克隆。
测试
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Tiger tiger = new Tiger("一只", 25, new Address("猪圈"));
Tiger cloneTiger = (Tiger)tiger.clone();
System.out.println(cloneTiger == tiger);
System.out.println(tiger);
System.out.println(cloneTiger);
System.out.println(cloneTiger.getAddress() == (tiger.getAddress()));
}
}
控制台输出:
false
Tiger(name=一只, age=25, address=Address(description=猪圈))
Tiger(name=一只, age=25, address=Address(description=猪圈))
true
结论:
1.第一行的布尔值为false表示两个Tiger确实是两个对象,再通过第二第三行,确实实现了克隆。
2.第三行布尔值为true表示两个Tiger中的Address对象为同一对象,并没有像Tiger一样实现克隆,而是直接指向同一Address对象内存地址。这就引出了深拷贝和浅拷贝。