深拷贝和浅拷贝
Object 类的clone() 方法 :
clone() 指创建并返回此对象的一个副本。(通过clone()方法返回一个新的对象,且新的对象的属性值从原对象拷贝)。
clone() 是 Object 类的方法,所以每一个类都会从Object类继承此方法。
如何实现clone : 实现 Cloneable 接口 , 重写clone() 方法
使用某个类的clone()方法,必须要实现 Cloneable 接口。否则会抛 java.lang.CloneNotSupportedException)因为Object 类本身不实现Cloneable接口,所以使用Object对象调用clone()方法时会抛出运行时异常 java.lang.CloneNotSupportedException。
Object 类的 clone()方法是 protected修饰的,所以要重写clone()方法,并且将 protected 改为 public修饰。
@Override public Object clone() throws CloneNotSupportedException { return super.clone(); }
从内存分析深拷贝和浅拷贝的区别,以Car和Wheel为例
package com.nemo.clone; // 车轮 类 public class Wheel { private String name; public Wheel(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } }
package com.nemo.clone; public class Car implements Cloneable { private int id; private String type; //汽车 和 车轮 是组合关系 private Wheel wheel; public Car (Wheel wheel) { this.wheel = wheel; } // 重写 clone() 方法 @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } public void setType(String type) { this.type = type; } public String getType() { return type; } public void setId(int id) { this.id = id; } public int getId() { return id; } public Wheel getWheel() { return wheel; } public void setWheel(Wheel wheel) { this.wheel = wheel; } }
package com.nemo.clone; public class Test { public static void main(String[] args) throws CloneNotSupportedException { Wheel wheel = new Wheel("XXX"); // 创建一个车轮 Car car = new Car(wheel); car.setId(123); car.setType("Audi"); car.getWheel().setName("AAA"); //将车轮的name改为 AAA Car cloneCar = (Car)car.clone(); // clone 一个Car cloneCar.setId(456); cloneCar.setType("Benz"); cloneCar.getWheel().setName("BBB"); // 通过cloneCar 将车轮的name 改为 BBB System.out.println(car); System.out.println(car.getId() + " " + car.getType()); System.out.println(car.getWheel()); System.out.println(car.getWheel().getName()); System.out.println("------------------------------------------"); System.out.println(cloneCar); System.out.println(cloneCar.getId() + " " + cloneCar.getType()); System.out.println(cloneCar.getWheel()); System.out.println(cloneCar.getWheel().getName()); } }
com.nemo.clone.Car@1fb8ee3 123 Audi com.nemo.clone.Wheel@61de33 BBB ------------------------------------------ com.nemo.clone.Car@14318bb 456 Benz com.nemo.clone.Wheel@61de33 BBB
从输出可以看出 car和拷贝出来的cloneCar的哈希码不同,car 的 id,type 和 cloneCar的id,type 也不同。(红色标识)
car的Wheel对象和cloneCar的Wheel对象哈希码相同,说明他们共享一个Wheel引用。所以当car更改了Wheel的name为"AAA"后cloneCar也更改Wheel的name为"BBB"最后car和cloneCar的name都输出BBB。(car和cloneCar共享一个引用 cloneCar后执行cloneCar.getWheel.setName("BBB") );
(哈希码 : 指的是JAVA通过某算法将对象的地址生成16进制的字符串。用于唯一标识某对象)
以上为浅拷贝。
下面是深拷贝的例子 :
package com.nemo.clone; // 车轮 类 public class Wheel implements Cloneable{ private String name; public Wheel(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } // 重写 clone() 方法 @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
package com.nemo.clone; public class Car implements Cloneable { private int id; private String type; //汽车 和 车轮 是组合关系 private Wheel wheel; public Car (Wheel wheel) { this.wheel = wheel; } // 重写 clone() 方法 @Override public Object clone() throws CloneNotSupportedException { Car object = (Car)super.clone(); object.wheel = (Wheel)wheel.clone(); return object; } public void setType(String type) { this.type = type; } public String getType() { return type; } public void setId(int id) { this.id = id; } public int getId() { return id; } public Wheel getWheel() { return wheel; } public void setWheel(Wheel wheel) { this.wheel = wheel; } }
package com.nemo.clone; public class Test { public static void main(String[] args) throws CloneNotSupportedException { Wheel wheel = new Wheel("XXX"); // 创建一个车轮 Car car = new Car(wheel); car.setId(123); car.setType("Audi"); car.getWheel().setName("AAA"); //将车轮的name改为 AAA Car cloneCar = (Car)car.clone(); // clone 一个Car cloneCar.setId(456); cloneCar.setType("Benz"); cloneCar.getWheel().setName("BBB"); // 通过cloneCar 将车轮的name 改为 BBB System.out.println(car); System.out.println(car.getId() + " " + car.getType()); System.out.println(car.getWheel()); System.out.println(car.getWheel().getName()); System.out.println("------------------------------------------"); System.out.println(cloneCar); System.out.println(cloneCar.getId() + " " + cloneCar.getType()); System.out.println(cloneCar.getWheel()); System.out.println(cloneCar.getWheel().getName()); } }
输出结果 :
com.nemo.clone.Car@1fb8ee3 123 Audi com.nemo.clone.Wheel@61de33 AAA ------------------------------------------ com.nemo.clone.Car@14318bb 456 Benz com.nemo.clone.Wheel@ca0b6 BBB
(红色标识的是与上一个例子不同的地方)
从输出结果可以看出 car和cloneCar的Wheel对象哈希码不同,说明car和cloneCar不再共享同一wheel对象。
(
采用clone()方法这种方式拷贝对象,其实并不好用。每一个类都需要实现cloneable接口,重写clone方法。当类的等级结构很深的时候会很乱。
另一种拷贝对象的方法是 采用序列化的方式。
// JAVA创建对象的几种方式
)