java克隆分为浅克隆和深克隆,概念如下:
浅拷贝(浅克隆)
克隆出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
深拷贝(深克隆)
克隆出来的所有变量都含有与原来的对象相同的值,那些引用其他对象的变量将指向复制出来的新对象,而不再是原有的那些被引用的对象。换言之,深克隆把要克隆的对象所引用的对象都克隆了一遍。
测试:
接下来我们新建两个实体类,一个为Person类,另一个为Father类,同时实现Clonable接口
Person.java
public class Person implements Cloneable {
private String name;
private int age;
private Father father;
public Person(String name,int age,Father father){
this.name = name;
this.age = age;
this.father = father;
}
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 Father getFather() {
return father;
}
public void setFather(Father father) {
this.father = father;
}
}
Father.java
public class Father implements Cloneable {
private String name;
private int age;
public Father(String name, int age){
this.name = name;
this.age = age;
}
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;
}
}
注:这里不能使用lombok注解
测试一:
为Person重写clone方法:
public Person(String name,int age,Father father){
this.name = name;
this.age = age;
this.father = father;
}
编写测试方法:
Father father = new Father("小明父亲",32);
Person person = new Person("小明",12,father);
Person personCloneable = person.clone();
personCloneable.setAge(1234);
personCloneable.setName("小刚");
personCloneable.getFather().setName("小刚父亲");
System.out.println(person.getName()+":"+person.getAge());
System.out.println(personCloneable.getName()+":" + personCloneable.getAge());
System.out.println(person.getFather().getName()+":"+personCloneable.getFather().getName());
System.out.println(person.getFather() == personCloneable.getFather());
输出结果:
小明:12
小刚:1234
小刚父亲:小刚父亲
true
从测试结果来看person对象的age和name属性成功被克隆,但father属性的引用未变化,因此这是一个浅克隆。
测试二
为Father类重写clone方法:
@Override
public Father clone() throws CloneNotSupportedException {
Father father = null;
father = (Father) super.clone();
return father;
}
为Person类重写clone方法:
@Override
public Person clone() throws CloneNotSupportedException {
Person person = null;
Father father = this.father.clone();
person = (Person) super.clone();
person.setFather(father);
return person;
}
再次使用测试一中的测试方法,结果如下:
小明:12
小刚:1234
小明父亲:小刚父亲
false
可以发现此时被克隆的对象的father对象引用和原对象中的father引用不相同,此时我们实现了深克隆。
扩展
如果要克隆的对象继承链比较长的话要实现深克隆,就必须逐层地实现Cloneable,这个过程是比较麻烦的,不过还有一种方法可以简便地实现深克隆。
serializable克隆
PersonSer.java
private String name;
private int age;
private FatherSer father;
public PersonSer serializableClone() throws IOException, ClassNotFoundException {
PersonSer person;
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
person = (PersonSer) oi.readObject();
return person;
}
public PersonSer(String name,int age,FatherSer father){
this.name = name;
this.age = age;
this.father = father;
}
@Override
public Person clone() throws CloneNotSupportedException {
Person person = null;
Father father = this.father.clone();
person = (Person) super.clone();
person.setFather(father);
return person;
}
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 FatherSer getFather() {
return father;
}
public void setFather(FatherSer father) {
this.father = father;
}
FatherSer.java
public class FatherSer implements Serializable {
private String name;
private int age;
@Override
public Father clone() throws CloneNotSupportedException {
Father father = null;
father = (Father) super.clone();
return father;
}
public FatherSer(String name, int age){
this.name = name;
this.age = age;
}
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 static void main(String[] args) throws IOException, ClassNotFoundException {
FatherSer father = new FatherSer("小明父亲",32);
PersonSer person = new PersonSer("小明",12,father);
PersonSer personCloneable = person.serializableClone();
personCloneable.setAge(1234);
personCloneable.setName("小刚");
personCloneable.getFather().setName("小刚父亲");
System.out.println(person.getName()+":"+person.getAge());
System.out.println(personCloneable.getName()+":" + personCloneable.getAge());
System.out.println(person.getFather().getName()+":"+personCloneable.getFather().getName());
System.out.println(person.getFather() == personCloneable.getFather());
}
输出结果:
小明:12
小刚:1234
小明父亲:小刚父亲
false
通过把对象写进ByteArrayOutputStream里,再把它读取出来。
注意这个过程中所有涉及的对象都必须实现Serializable接口,由于涉及IO操作,这种方式的效率会比前面的低。