本文共 7397 字,大约阅读时间需要 24 分钟。
Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = new Student(); stu2.setNumber(stu1.getNumber());
浅克隆: 当对象被复制时,只复制对象本身和其中包含的值类型成员变量,而引用类型的成员对象并没有复制
public class Student implements Cloneable { private int number; public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } //实现clone接口 @Override public Object clone() { Student stu = null; try { //浅克隆 stu = (Student) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return stu; } //测试克隆 public static void main(String[] args) { Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = (Student) stu1.clone(); System.out.println("学生1: "+stu1.getNumber() + " " + stu1.hashCode()); System.out.println("学生2: "+stu2.getNumber() + " " + stu2.hashCode()); stu2.setNumber(54321); System.out.println("学生1: "+stu1.getNumber() + " " + stu1.hashCode()); System.out.println("学生2: "+stu2.getNumber() + " " + stu2.hashCode()); }}
运行结果:
学生1: 12345 1580066828学生2: 12345 491044090学生1: 12345 1580066828学生2: 54321 491044090
从程序的运行结果可以看出,学生2修改值类型变量(如int)number后,自身number值发生改变,但并不影响学生1
深克隆: 将原型对象的所有引用对象也复制一份给克隆对象
public class Student implements Cloneable { private int number; private Address add; public void setAdd(Address add) { this.add = add; } private Address getAdd(){ return this.add; } public void setNumber(int number){ this.number = number; } public int getNumber(){ return this.number; } @Override public Object clone() { Student stu = null; try { //浅复制 stu = (Student) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } //深度复制 stu.add = (Address) add.clone(); return stu; } public static void main(String[] args) { Address add = new Address(); add.setAdd("北京市"); Student stu1 = new Student(); stu1.setNumber(12345); stu1.setAdd(add); Student stu2 = (Student) stu1.clone(); System.out.println("学生1: " + stu1.getNumber() + ",地址: " + stu1.getAdd().getAdd() + ", " + stu1.hashCode()); System.out.println("学生2: " + stu2.getNumber() + ",地址: " + stu2.getAdd().getAdd() + ", " + stu2.hashCode()); stu2.setNumber(54321); add.setAdd("杭州市"); System.out.println("学生1: " + stu1.getNumber() + ",地址: " + stu1.getAdd().getAdd() + ", " + stu1.hashCode()); System.out.println("学生2: " + stu2.getNumber() + ",地址: " + stu2.getAdd().getAdd() + ", " + stu1.hashCode()); }}class Address implements Cloneable { public String add; @Override protected Object clone() { Address add = null; try { add = (Address) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return add; } public void setAdd(String add) { this.add = add; } public String getAdd(){ return this.add; }}
运行结果:
学生1: 12345,地址: 北京市, 1580066828学生2: 12345,地址: 北京市, 491044090学生1: 12345,地址: 杭州市, 1580066828学生2: 54321,地址: 北京市, 1580066828
当去掉上述代码中的stu.add = (Address) add.clone();
时,运行结果如下。可以看出引用类型对象Address add并未复制到克隆对象中
学生1: 12345,地址: 北京市, 1580066828学生2: 12345,地址: 北京市, 491044090学生1: 12345,地址: 杭州市, 1580066828学生2: 54321,地址: 杭州市, 1580066828
通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
Car类import java.io.Serializable;public class Car implements Serializable { private static final long serialVersionUID = 6880783148707693267L; //品牌 private String brand; //最高时速 private int maxSpeed; public Car(String brand, int maxSpeed) { this.brand = brand; this.maxSpeed = maxSpeed; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public int getMaxSpeed() { return maxSpeed; } public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; }}
Person类
import java.io.Serializable;public class Person implements Serializable { private static final long serialVersionUID = 7592930394427200495L; //姓名 private String name; //年龄 private int age; //座驾 private Car car; public Person(String name, int age, Car car) { this.name = name; this.age = age; this.car = car; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; }}
CloneUtil类
import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class CloneUtil { public staticT clone(T obj) throws Exception { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bout); oos.writeObject(obj); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bin); return (T) ois.readObject(); }}
CloneTest类
public class CloneTest { public static void main(String[] args) throws Exception { Person person = new Person("lisi", 18, new Car("BMW", 300)); Person person1 = CloneUtil.clone(person); System.out.println("person car before:" + person.getCar().getBrand() + " " + person.hashCode() + " " + person.getCar().hashCode()); System.out.println("person1 car before:" + person1.getCar().getBrand() + " " + person1.hashCode() + " " + person1.getCar().hashCode()); person1.getCar().setBrand("BYD"); System.out.println("person car after:" + person.getCar().getBrand() + " " + person.hashCode() + " " + person.getCar().hashCode()); System.out.println("person1 car after:" + person1.getCar().getBrand() + " " + person1.hashCode() + " " + person1.getCar().hashCode()); }}
运行结果
person car before:BMW 895328852 1304836502person1 car before:BMW 824909230 122883338person car after:BMW 895328852 1304836502person1 car after:BYD 824909230 122883338
备注1: 修改克隆的Person 对象person1 关联的 汽车对象 的品牌属性,原来的Person对象person关联的汽车不会受到任何影响,因为在克隆Person对象时其关联的Car对象也被克隆了
备注2: 基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过范型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译期完成的,不是在运行时抛出异常,这种方案明显优于使用Object类的clone方法克隆对象
使用BeanUtils
try { Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = new Student(); BeanUtils.copyProperties(stu2, stu1);} catch (IllegalAccessException e) { e.printStackTrace();} catch (InvocationTargetException e) { e.printStackTrace();}
使用PropertyUtils
try { Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = new Student(); PropertyUtils.copyProperties(stu2, stu1); catch (NoSuchMethodException e) { e.printStackTrace(); }
两者区别:
BeanUtils提供了类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行自动转换;而PropertyUtils不支持这个功能,但是速度会更快一些。在实际开发中,BeanUtils使用更普遍,犯错的风险更低转载地址:http://ketii.baihongyu.com/