克隆(复制)在Java中是一种常见的操作,目的是快速获取一个对象副本。克隆分为深克隆和浅克隆。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
浅拷贝案例
public class Cat implements Cloneable{
private String name;
private Skill skill;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @return the skill
*/
public Skill getSkill() {
return skill;
}
/**
* @param skill the skill to set
*/
public void setSkill(Skill skill) {
this.skill = skill;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
public class Skill {
private String name = "小猫";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void onTree() {
System.out.println(name+"会上树");
}
public void catchMouse() {
System.out.println(name+"会抓老鼠");
}
}
Cat cat = new Cat();
cat.setName("白猫");
cat.setSkill(new Skill());
Cat catClone = (Cat) cat.clone();
System.out.println("更改浅拷贝非基本类型数据前:"+cat.getSkill().getName());
catClone.getSkill().setName("大猫");
System.out.println("更改浅拷贝非基本类型数据后:"+cat.getSkill().getName());
System.out.println(cat.getName() == catClone.getName());
System.out.println(cat.getSkill() == catClone.getSkill());
结果:
更改浅拷贝非基本类型数据前:小猫
更改浅拷贝非基本类型数据后:大猫
true
true
被克隆的对象和克隆出来的对象 其 对象中的引用对象 Skill , 其指向的是同一地址,这样子的克隆被称之为 浅克隆,拷贝与被拷贝中的非八种基本数据类型的引用对象进行修改,2个对象都受影响。
浅克隆只克隆对象中的基本数据类型,包括 byte、short、int、long、float、double、boolean、char、String (八种基本数据类型和一个引用类型),而对象中的其他非String引用对象类型则不会被克隆
深拷贝案例
我们改造一下上面的浅拷贝:
Skill实现Cloneable接口,并重写clone方法:
public class Skill implements Cloneable{
private String name = "小猫";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void onTree() {
System.out.println(name+"会上树");
}
public void catchMouse() {
System.out.println(name+"会抓老鼠");
}
public Skill clone() {
Skill clone = null;
try {
clone = (Skill) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
Cat类扩展下,使对象的引用也克隆:
public Object clone() {
Cat clone = null;
try {
clone = (Cat) super.clone();
clone.setSkill(this.getSkill().clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
运行结果:
更改深拷贝非基本类型数据前:小猫
更改深拷贝非基本类型数据后:小猫
true
false
可以看出,深拷贝改其中一个对象,另一个不受影响。
对象序列化拷贝
如引用类型属性有多层引用类型属性关系,或者有的时候我们的对象中的对象 如果不是那么方便的访问其属性,不能够对其clone方法进行改写的时候,这个时候应该使用字节流加上序列化的方法完成对对象的深克隆。
注意:序列化拷贝即使八种基本类型也是不一样的。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Cat implements Cloneable,Serializable{
private String name;
private Skill skill;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @return the skill
*/
public Skill getSkill() {
return skill;
}
/**
* @param skill the skill to set
*/
public void setSkill(Skill skill) {
this.skill = skill;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
public Object clone() {
ByteArrayOutputStream bos = null ;
ObjectOutputStream oos = null ;
ByteArrayInputStream bis = null ;
ObjectInputStream ois = null ;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream( bos.toByteArray() );
ois = new ObjectInputStream( bis );
Cat copy = (Cat) ois.readObject();
return copy;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}finally{
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
import java.io.Serializable;
public class Skill implements Serializable{
private String name = "小猫";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void onTree() {
System.out.println(name+"会上树");
}
public void catchMouse() {
System.out.println(name+"会抓老鼠");
}
}
运行结果:
更改深拷贝非基本类型数据前:小猫
更改深拷贝非基本类型数据后:小猫
false
false