Java深克隆和浅克隆的使用和区别
背景
Java中想要复制对象时,需要考虑到一个问题:如果是引用类型对象,如果只是通过一般的“=”赋值,那么因为只是把引用地址给了新对象,所以改变新对象时会因为实际改变的引用地址指向的对象,因此难免出现改变源对象的属性。例如:
public class Demo1 { static class People { private String name; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } @Override public String toString() { return "People{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}'; } } public static void main(String[] args) { People people1 = new People(); people1.setName("zhangsan"); people1.setAge("23"); System.out.println("源对象:" + people1); People people2 = people1; System.out.println("新对象:" + people2); people2.setName("lisi"); System.out.println("修改后的源对象:" + people1); System.out.println("修改后的新对象:" + people2); } }
结果:
源对象:People{name='zhangsan', age='23'} 新对象:People{name='zhangsan', age='23'} 修改后的源对象:People{name='lisi', age='23'} 修改后的新对象:People{name='lisi', age='23'}
因此这时就需要考虑解决办法,如何把源对象的属性给新对象,而且两个对象改变属性时又可以不相互影响?
概念
Java复制对象的实现方式中有个“克隆”的概念,就是复制一个对象的副本,而克隆又分浅克隆和深克隆。
- 浅克隆:克隆一个新对象,但是属性如果是引用类型对象则用的同一个引动地址指向的对象。
- 深克隆:克隆一个新对象,包括引用类型的属性内存地址指向的也是新创建的对象。
实现步骤:
- 实现Clonable接口;
- 重写clone()方法。
- 调用clone()方法克隆对象。
浅克隆
1、验证是否为新对象
bean(未重写toString(),用来验证克隆出的对象是否为新的)
public class People implements Cloneable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //修改权限为:public @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
main
public static void main(String[] args) throws CloneNotSupportedException { People old = new People(); old.setName("zhangsan"); old.setAge(23); People clone = (People) old.clone(); System.out.println("源对象:" + old); System.out.println("新对象:" + clone); }
结果:地址不同,所以clone的新对象和原来的不是同一个
源对象:com.company.bean.People@4554617c
新对象:com.company.bean.People@74a14482
2、验证引用类型属性是否为新的
Car
public class Car implements Cloneable { private String name; private String color; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
People
public class People implements Cloneable { private String name; private int age; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } //修改权限为:public @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "People{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
main
public static void main(String[] args) throws CloneNotSupportedException { People old = new People(); old.setName("zhangsan"); old.setAge(23); Car car = new Car(); car.setName("ferrari"); car.setColor("red"); old.setCar(car); People clone = (People) old.clone(); System.out.println("源对象:" + old); System.out.println("新对象:" + clone); }
结果:虽然对象是新的,但是clone的属性还是用一个。
源对象:People{name='zhangsan', age=23, car=com.company.bean.Car@4554617c}
新对象:People{name='zhangsan', age=23, car=com.company.bean.Car@4554617c}
深克隆:重写对象中引用类型属性clone()
Car
public class Car implements Cloneable { private String name; private String color; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } //重写clone @Override public Car clone() throws CloneNotSupportedException { return (Car) super.clone(); } }
People
public class People implements Cloneable { private String name; private int age; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } //重写clone方法 @Override public People clone() throws CloneNotSupportedException { People people = (People) super.clone(); people.car = car.clone(); return people; } @Override public String toString() { return "People{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
main
public static void main(String[] args) throws CloneNotSupportedException { People old = new People(); old.setName("zhangsan"); old.setAge(23); Car car = new Car(); car.setName("ferrari"); car.setColor("red"); old.setCar(car); People clone = (People) old.clone(); System.out.println("源对象:" + old); System.out.println("新对象:" + clone); }
结果:引用类型属性也是新的
源对象:People{name='zhangsan', age=23, car=com.company.bean.Car@4554617c}
新对象:People{name='zhangsan', age=23, car=com.company.bean.Car@74a14482}
补充:序列化方式实现
Car
public class Car implements Serializable { private String name; private String color; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
People
public class People implements Serializable { private String name; private int age; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "People{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
main
public static void main(String[] args) throws IOException, ClassNotFoundException { People old = new People(); old.setName("zhangsan"); old.setAge(23); Car car = new Car(); car.setName("ferrari"); car.setColor("red"); old.setCar(car); System.out.println("源对象:" + old); //序列化 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(outputStream); oos.writeObject(old); //反序列化 ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); ObjectInputStream ois = new ObjectInputStream(inputStream); People clone = (People) ois.readObject(); System.out.println("新对象:" + clone); }
结果:和深克隆一样,对象和引用类型属性的地址都是新的。
源对象:People{name='zhangsan', age=23, car=com.company.bean.Car@4554617c}
新对象:People{name='zhangsan', age=23, car=com.company.bean.Car@2d98a335}