Java浅克隆和深克隆
一、何为克隆
在Java的体系中,数据类型分为基本数据类型和引用数据类型。
基本数据类型包括byte,short,int,long,float,double,boolean,char 8种,其克隆可通过赋值运算实现,比如
int a = 1; int b = a;
引用类型的克隆的实现方式有以下两种:
1)实现Cloneable接口,重写clone() 方法,修改clone() 方法的修饰符为public。其分为浅克隆和深克隆。
2) 实现Serializable接口,对实例进行序列化,通过二进制流反序列化。其为真正的克隆。
注:若类没实现Cloneable接口,调用对象的clone方法会抛出CloneNotSupportedException异常
二、重写clone实现克隆
克隆就是获得当前对象的副本,其与当前对象是内存地址不同的两个对象。
浅克隆指的是当前对象的实例成员为引用类型时,副本的该实例成员只是复制了其引用值,指向同一个对象。
深克隆则是对该引用指向的对象进行克隆,然后引用指向了该副本,指向不同的对象。
浅克隆导致了一个问题就是,对实例或克隆副本做出的改变会影响彼此,从而导致错误的结果,下面的例子可以说明:
汽车类 Car,包含一个引用类型成员brand:
/** * Car类 * 实现Cloneable接口,重写clone方法 * @author zhangyj * */ public class Car implements Cloneable{ //使用年限 private int year; //品牌 private Brand brand; public int getYear() { return year; } public void setYear(int year) { this.year = year; } public Brand getBrand() { return brand; } public void setBrand(Brand brand) { this.brand = brand; } //构造器 public Car(int year, Brand brand) { this.year = year; this.brand = brand; } /** * 浅克隆,调用父类的clone方法 */ @Override public Object clone() { Object obj = null; try { obj = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return obj; } /** * 深克隆,其引用类型成员也要调用clone * @return */ public Car deepClone() { Car car = null; try { car = (Car)super.clone(); car.setBrand((Brand)brand.clone()); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return car; } @Override public String toString() { return "[year=" + year + ", brand=" + brand + "]"; } } /** * 品牌类 * @author zhangyj * */ class Brand implements Cloneable{ //名称 private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override protected Object clone() { Object obj = null; try { obj = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return obj; } public Brand(String name) { this.name = name; } @Override public String toString() { return "Brand [name=" + name + "]"; } }
测试类 CarCloneTest :
public class CarCloneTest { public static void main(String[] args) { Brand brand = new Brand("BMW"); Car car = new Car(5, brand); Car carClone; carClone = (Car) car.clone(); //(1) //carClone = (Car) car.deepClone(); //(2) System.out.println("******************************************"); System.out.println("car "+car); System.out.println("carClone "+carClone); System.out.println("******************************************"); System.out.println("将car年限改为 7,品牌名称改为Banz"); brand.setName("Banz"); car.setYear(7); System.out.println("******************************************"); System.out.println("car "+car); System.out.println("carClone "+carClone); } }
浅克隆测试,运行(1)代码,得到结果如下:
****************************************** car [year=5, brand=Brand [name=BMW]] carClone [year=5, brand=Brand [name=BMW]] ******************************************
将car年限改为 7,品牌名称改为Banz ****************************************** car [year=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=Banz]]
可见当修改car中brand对象的名称为Banz时,carClone也跟着改变,可见其引用的brand对象为同一个,显然这不是我们想要的,而深克隆就解决了这个问题。
深克隆测试,运行(2)代码,得到结果如下:
****************************************** car [year=5, brand=Brand [name=BMW]] carClone [year=5, brand=Brand [name=BMW]] ******************************************
将car年限改为 7,品牌名称改为Banz ****************************************** car [year=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=BMW]]
三、序列化实现深克隆
序列化对象必须实现Serializable接口,使对象可序列化。
public class Car implements Serializable{ private static final long serialVersionUID = 7883197573658810857L; private int year; //使用年限 private Brand brand; //品牌 public int getYear() { return year; } public void setYear(int year) { this.year = year; } public Brand getBrand() { return brand; } public void setBrand(Brand brand) { this.brand = brand; } public Car(int year, Brand brand) { this.year = year; this.brand = brand; } @Override public String toString() { return "[year=" + year + ", brand=" + brand + "]"; } } class Brand implements Serializable{ private static final long serialVersionUID = -6505899377489945908L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Brand(String name) { this.name = name; } @Override public String toString() { return "Brand [name=" + name + "]"; } }
克隆工具类 CloneUtil:
public final class CloneUtil { private CloneUtil() { throw new AssertionError(); } /** * 序列化实现深克隆 * @param t 泛型对象 * @return */ @SuppressWarnings("unchecked") public static <T> T deepClone(T t) { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream objOut; ObjectInputStream objIn; T tClone = null ; try { objOut = new ObjectOutputStream(out); objOut.writeObject(t); //将对象以二进制流形式写入 ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); objIn = new ObjectInputStream(in); tClone = (T)objIn.readObject(); //反序列化读取对象 } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return tClone; } }
测试类 CarCloneTest:
public class CarCloneTest { public static void main(String[] args) { Brand brand = new Brand("BMW"); Car car = new Car(5, brand); Car carClone; // carClone = (Car) car.clone(); //(1) // carClone = (Car) car.deepClone(); //(2) carClone = CloneUtil.deepClone(car); System.out.println("******************************************"); System.out.println("car "+car); System.out.println("carClone "+carClone); System.out.println("******************************************"); System.out.println("将car年限改为 7,品牌名称改为Banz"); brand.setName("Banz"); car.setYear(7); System.out.println("******************************************"); System.out.println("car "+car); System.out.println("carClone "+carClone); } }
运行测试程序,得到结果如下:
****************************************** car [year=5, brand=Brand [name=BMW]] carClone [year=5, brand=Brand [name=BMW]] ******************************************
将car年限改为 7,品牌名称改为Banz
******************************************
car [year=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=BMW]]
可见通过反序列化对象进行克隆也能得到我们想要的结果。
以上!