JVM实现拷贝的目的:
大家先思考一个问题,为什么需要克隆对象?直接 new 一个对象不行吗?
答案是:克隆的对象可能包含一些已经修改过的属性,而 new 出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的 “状态” 就靠 clone 方法了。那么我把这个对象的临时属性一个一个的赋值给我新 new 的对象不也行嘛?
可以是可以,但是一来麻烦不说,二来,大家通过上面的源码都发现了 clone 是一个 native 方法,就是快啊,在底层实现的
引用的拷贝
//引用拷贝
private static voidcopyReferenceObject(){
Person p= new Person(23, "zhang");
Person p1=p;
System.out.println(p);
System.out.println(p1);
}
这里打印的结果:
Person@3654919e
Person@3654919e
可以看到,打印的结果是一样的,也就是说,二者的引用是同一个对象,并没有创建出一个新的对象。因此要区分引用拷贝和对象拷贝的区别,下面要介绍的就是对象拷贝。
浅拷贝
如果pojo中存在的是基本数据类型 ,String 除外 ,实现Cloneable 覆写Clone 方法 这个就是浅拷贝
- code
packagecore.java.deeporshoawcopy;/***@authorDGW-PC
* @date 2018年6月7日
*@see验证 浅拷贝 一般要求实体类使用包装类型 对于深拷贝 类中存在对其他类的引用,也需要实现cloneable接口*/
class Person implementsCloneable{privateString name;privateInteger age;publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicInteger getAge() {returnage;
}public voidsetAge(Integer age) {this.age =age;
}
@Overrideprotected Object clone() throwsCloneNotSupportedException {/*Person p=null;
try{
p=(Person) super.clone();
}catch (CloneNotSupportedException e) {
}*/
return super.clone();
}
}public classBase {public static void main(String[] args) throwsCloneNotSupportedException {
Person person= newPerson();
person.setName("a");
person.setAge(12);
Person per1=(Person) person.clone();
per1.setName("b");
per1.setAge(14);;
System.out.println(person.getName()+" "+person.getAge().hashCode(0));
System.out.println(per1.getName()+" "+per1.getAge());
}
}
内存图:
深拷贝
- 如果你的POJO 存在的不是基本上数据类型,可以是自己定义类型,也可以其他包提供的类型 这里以java 提供的Data 的为例子 可以看下面的代码 自身实现clone 方法 你在涉及到使用拷贝的时候一定要注意别的包提供的类是否出现了问题
/*** Return a copy of this object.*/
publicObject clone() {
Date d= null;try{
d= (Date)super.clone();if (cdate != null) {
d.cdate=(BaseCalendar.Date) cdate.clone();
}
}catch (CloneNotSupportedException e) {} //Won't happen
returnd;
}
- 下面介绍一下基本深拷贝的代码 很短 : 你一定主要 主类包装了多少其他的引用类型的其他类,那么其他必须都要实现Cloneable 接口 以及clone (保护方法) 方法
方法原型:
仔细一看,它还是一个 native 方法,大家都知道 native 方法是非 Java 语言实现的代码,供 Java 程序调用的,
因为 Java 程序是运行在 JVM 虚拟机上面的,要想访问到比较底层的与操作系统相关的就没办法了,只能由靠近操作系统的语言来实现
1/*2Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
3The general intent is that, for any object x, the expression:
41) x.clone() != x will be true
52) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
63) x.clone().equals(x) will be true, this is not an absolute requirement.
7*/8protected native Object clone() throws CloneNotSupportedException;
需要满足的条件:
对于任何对象x,表达式:
1)x.clone()!= x将为真
2)x.clone()。getClass()== x.getClass()为true,但这不是绝对要求。
3)x.clone()。equals(x)将为true,这不是绝对要求。
packagecore.java.deeporshoawcopy;/***@authorDGW-PC
* @date 2018年6月7日
*@see实现序列化 深拷贝*/
class Dog implementsCloneable{privateString name;publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}
@Overrideprotected Object clone() throwsCloneNotSupportedException {return super.clone();
}
}class User implementsCloneable{privateString name;privateDog dog;publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicDog getDog() {returndog;
}public voidsetDog(Dog dog) {this.dog =dog;
}
@Overrideprotected Object clone() throwsCloneNotSupportedException {
User u=(User) super.clone();
u.dog=(Dog) dog.clone(); //多个需要在全部把关系搞清楚
returnu;
}
}public classObjCloner {public static void main(String[] args) throwsCloneNotSupportedException {
Dog dog= newDog();
dog.setName("田园犬");
User user= newUser();
user.setDog(dog);
user.setName("王二");
User user1=(User) user.clone();
user1.setName("张三");
Dog dog2= newDog();
dog2.setName("德国牧羊犬");
user1.setDog(dog2);
System.out.println(user.getName()+"养了"+user.getDog().getName());
System.out.println(user1.getName()+"养了"+user1.getDog().getName());
}
}
结果:
类组合形式下深拷贝
class Car implementsCloneable{
String name;publicCar(String name) {this.name =name;
}
@OverrideprotectedObject clone() {
Car car=null;try{
car=(Car) super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}returncar;
}
@OverridepublicString toString() {return "Car [name=" + name + "]";
}
}class Home implementsCloneable{
String name;publicHome(String name) {this.name =name;
}
@OverrideprotectedObject clone() {
Home home=null;try{
home=(Home) super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}returnhome;
}
@OverridepublicString toString() {return "Home [name=" + name + "]";
}
}class Wife implementsCloneable{
String name;publicWife(String name) {this.name =name;
}
@OverrideprotectedObject clone() {
Wife wife=null;try{
wife=(Wife) super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}returnwife;
}
@OverridepublicString toString() {return "Wife [name=" + name + "]";
}
}class Person implementsCloneable{
String name;
Car car;
Home home;
Wife wife;publicPerson(String name, Car car, Home home, Wife wife) {super();this.name =name;this.car =car;this.home =home;this.wife =wife;
}
@OverridepublicString toString() {return "Person [name=" + name + ", car=" + car + ", home=" + home + ", wife=" + wife + "]";
}
@OverrideprotectedObject clone() {
Person person=null;try{
person=(Person) super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
person.car=(Car) this.car.clone();
person.home=(Home) this.home.clone();
person.wife=(Wife) this.wife.clone();returnperson;
}
}public classTest2 {public static voidmain(String[] args) {
Person person= new Person("Tom", new Car("bmw"), new Home("一环以内"), new Wife("intkk"));//Person person1=person;
Person person1 =(Person) person.clone();
System.out.println(person);
System.out.println(person1);
person1.name="Jerry";
person1.home= new Home("帝国");
person1.car=new Car("五菱骨灰盒");
System.out.println(person);
System.out.println(person1);
}
}
问题: 当存在多个类的时候,每个类都要实现Clonebale接口,实现过于复杂: 特别是下面这段代码: (当多个类的时候建议使用串行化方式进行clone)
protectedObject clone() {
Person person=null;try{
person=(Person) super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
person.car=(Car) this.car.clone();
person.home=(Home) this.home.clone();
person.wife=(Wife) this.wife.clone();returnperson;
}
串行化方式实现深拷贝
实现方法:
实现Serializable接口,如果类中存在组合形式的使用,那么每个类都要实现Serializable接口
- 以下代码着重注意一下 CloneObj方法 ,就行。
packagecore.java.deeporshoawcopy;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.io.Serializable;/***@authorDGW-PC
* @date 2018年6月7日
*@since串行化 实现 深拷贝*/
class Body implementsSerializable{/****/
private static final long serialVersionUID = 1L;privateString name;privateFonter fonter;privateHead head;publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicFonter getFonter() {returnfonter;
}public voidsetFonter(Fonter fonter) {this.fonter =fonter;
}publicHead getHead() {returnhead;
}public voidsetHead(Head head) {this.head =head;
}
@OverridepublicString toString() {return "Body [name=" + name + ", fonter=" + fonter + ", head=" + head + "]";
}publicBody(String name, Fonter fonter, Head head) {super();this.name =name;this.fonter =fonter;this.head =head;
}
}class Head implementsSerializable{/****/
private static final long serialVersionUID = 1L;privateInteger size;
}class Fonter implementsSerializable{/****/
private static final long serialVersionUID = 1L;privateInteger size;
}class Face implementsSerializable{/****/
private static final long serialVersionUID = 1L;privateInteger size;
}public classObjClonerSeiz {private static T CloneObj(T obj){
T retobj=null;try{//写入流中
ByteArrayOutputStream baos = newByteArrayOutputStream();
ObjectOutputStream oos= newObjectOutputStream(baos);
oos.writeObject(obj);//从流中读取
ObjectInputStream ios = new ObjectInputStream(newByteArrayInputStream(baos.toByteArray()));
retobj=(T) ios.readObject();
}catch(Exception e) {
e.printStackTrace();
}returnretobj;
}public static voidmain(String[] args) {
Body body= new Body("张三", new Fonter(), newHead());
Body body2=CloneObj(body);
System.out.println("body==body2 ====>"+(body==body2));
System.out.println("body.font==body2.font ====>"+(body.getFonter()==body2.getFonter()));
System.out.println("body.head==body2.head ====>"+(body.getHead()==body2.getHead()));
}
}
总结:
在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。
(如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。)
实现对象克隆有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆