克隆的目的:快速创建一个已有对象的副本。
克隆的步骤:
创建一个对象
将原有对象的数据导入到新创建的数据中
clone方法首先会判对象是否实现了Cloneable接口,若无则抛出CloneNotSupportedException, 最后会调用internalClone. intervalClone是一个native方法,一般来说native方法的执行效率高于非native方法。
源码:
当某个类要复写clone方法时,要继承Cloneable接口。通常的克隆对象都是通过super.clone()方法来克隆对象。
2.浅克隆(shadow clone)
克隆就是复制一个对象的复本.若只需要复制对象的字段值(对于基本数据类型,如:int,long,float等,则复制值;对于复合数据类型仅复制该字段值,如数组变量则复制地址,对于对象变量则复制对象的reference。
实现Cloneable接口的类都具备被拷贝的能力,拷贝在内存中进行,比new生成对象性能明显提升;
clone方法是Object类的protected方法,也就是在用户编写的代码中不能直接调用。为此必须重写定义clone方法,并声明为public,
Cloneable接口的出现与接口正常使用没有关系,是一个空接口,它只是一个标记表示一个对象需要克隆,如果一个对象需要克隆,而没有实现Cloneable接口,就会产生一个CloneNotSupportedException异常
Clone()方法存在与Object对象中,但是其不会将对象的全部属性都拷贝,而是有选择的拷贝基本类型:对象:如果变量是一个实例对象,只拷贝地址引用;
String字符串:拷贝地址引用,但是修改时,会从字符串池中重新生成新的字符串,原来的保持不变;对于Clone实现对象的拷贝:需要新建大量的对象,工程量大可以使用序列化来实现对象的拷贝
需要clone的对象:
packagecom.gzu.pyu.thinking.in.java.practice0211;importjava.io.Serializable;public class Student implementsCloneable ,Serializable {publicString name;public intage;publicAddress address;publicStudent() {
}public Student(String name, intage, Address address) {this.name =name;this.age =age;this.address =address;
}publicAddress getAddress() {returnaddress;
}public voidsetAddress(Address address) {this.address =address;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}public intgetAge() {returnage;
}public void setAge(intage) {this.age =age;
}
@OverridepublicObject clone() {
Student student=null;try{//淺拷貝
student= (Student)super.clone();
}catch(CloneNotSupportedException e) {return newStudent();
}//支持深度克隆
student.address=address.clone();returnstudent;
}
@OverridepublicString toString() {final StringBuffer sb = new StringBuffer("Student{");
sb.append("name='").append(name).append('\'');
sb.append(", age=").append(age);
sb.append(", address=").append(address);
sb.append('}');returnsb.toString();
}
}class Address implementsCloneable,Serializable{
String city;
String town;publicAddress() {
}publicAddress(String city, String town) {this.city =city;this.town =town;
}publicString getCity() {returncity;
}public voidsetCity(String city) {this.city =city;
}publicString getTown() {returntown;
}public voidsetTown(String town) {this.town =town;
}
@OverridepublicAddress clone() {
Address address=null;try{
address= (Address) super.clone();
}catch(CloneNotSupportedException e) {return newAddress();
}returnaddress;
}
@OverridepublicString toString() {final StringBuffer sb = new StringBuffer("Address{");
sb.append("city='").append(city).append('\'');
sb.append(", town='").append(town).append('\'');
sb.append('}');returnsb.toString();
}
}
测试方法如下:
packagecom.gzu.pyu.thinking.in.java.practice0211;importcom.gzu.pyu.thinking.in.java.utils.CloneUtils;public classTestMain {public static voidmain(String ... args){
Address address=new Address("ShengZheng","LongGang");//将address指向的区域赋值给addressNew,使得addressNew和address同时指向了同一个区域
Address addressNew=address;//address指向的内容改变了,addressNew指向的内容也跟着改变
System.out.println(address==addressNew);//true
address.setCity("GuiYang");
System.out.println(address);//Address{city='GuiYang', town='LongGang'}
System.out.println(addressNew);//Address{city='GuiYang', town='LongGang'}//address進行克隆,將address進行了复制了,产生了一个新的对象
Address addressClone =address.clone();
System.out.println(address==addressClone);//false
address.setTown("HuaXi");
System.out.println(address);//Address{city='GuiYang', town='HuaXi'}
System.out.println(addressClone);//Address{city='GuiYang', town='LongGang'}//在内存中通过字节流的拷贝是比较容易的,把木对象写入到一个字节流中,再从字节流中读出来
Address addr =CloneUtils.clone(address);
System.out.println(address==addr);//false
addr.setCity("PanZhou");
System.out.println(address);//Address{city='GuiYang', town='HuaXi'}
System.out.println(addr);//Address{city='PanZhou', town='HuaXi'}
}
}
二、使用序列化来实现对象的拷贝
在内存中通过字节流的拷贝是比较容易的,把木对象写入到一个字节流中,再从字节流中毒出来,这样可以创建一个新的对象,且新对象与木对象不存在引用共享的问题,实现的对象的深拷贝
packagecom.gzu.pyu.thinking.in.java.utils;import java.io.*;/*** 在内存中通过字节流的拷贝是比较容易的,把木对象写入到一个字节流中,再从字节流中读出来,
* 这样可以创建一个新的对象,且新对象与木对象不存在引用共享的问题,实现的对象的深拷贝*/
public classCloneUtils {
@SuppressWarnings("unchecked")public static T clone(T obj){
T cloneObj= null;//写入字节流
try{
ByteArrayOutputStream out= newByteArrayOutputStream();
ObjectOutputStream obs= newObjectOutputStream(out);
obs.writeObject(obj);
obs.close();//分配内存,写入原始对象,生成新对象
ByteArrayInputStream ios = newByteArrayInputStream(out.toByteArray());
ObjectInputStream ois= newObjectInputStream(ios);//返回生成的新对象
cloneObj =(T) ois.readObject();
ois.close();
}catch(IOException e){
e.printStackTrace();
}catch(ClassNotFoundException e) {
e.printStackTrace();
}returncloneObj;
}
}