Java拷贝-深拷贝与浅拷贝
一、出现原因
在项目中经常需要复制一个完全一样的对象,然后再对新对象进行更新等操作而不影响老对象。
而以以下方式获取是否会出现问题呢?
User user = new User(); User copyUser = user;
答案是肯定的,上面的方法不能称之为复制对象,更准确地说应该是复制引用,因为user和copyUser指向的是内存堆里的同一个对象:
user ¦→ Object
copyUser ¦→ ↑
栈区(引用) ¦ 堆区(对象)
那么如何才能实现呢?应该使用Java中的拷贝(Object Copy),主要分为:浅拷贝 (Shallow Copy)、深拷贝 (Deep Copy),用的方法为clone()。
二、浅拷贝与深拷贝的区别
1、浅拷贝:
类实现默认的Object.clone()方法,拷贝对象时,
(1)对于引用类型的成员变量(属性)拷贝只是拷贝“值”即地址(引用),没有在堆中开辟新的内存空间;
(2)对于字段类型是值类型(基本类型)的,那么对该字段进行复制。
2、深拷贝:
类重写clone()方法,对于引用类型成员变量,重新在堆中开辟新的内存空间,简单地说,将对象的全部属性内容都拷贝一份新的。
3、使用:
如果复制对象只含基本数据类型,则使用浅拷贝即可满足;若果包含成员对象,则需使用深拷贝。
三、浅拷贝
一般实现方法:
-
被复制的类需要实现 Clonenable 接口(不实现的话在调用 clone 方法会抛出 CloneNotSupportedException 异常) 该接口为标记接口 (不含任何方法)
-
覆盖 clone () 方法,方法中调用 super.clone () 方法得到需要的复制对象,(native 为本地方法)
public class Person implements Cloneable{ private String name; private int age; private Address address; //构造方法 public Person(String name, int age, Address address) { this.name = name; this.age = age; this.address = address; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public String display(){ return "Person[name="+name+",age="+age+",address"+address+"]"; } 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 Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } public class Address { private String province;//省份 private String city;//所在城市 //构造方法 public Address(String province, String city) { this.province = province; this.city = city; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return "Address[province="+province+",city="+city+"]"; } } public class Test { public static void main(String[] args) throws CloneNotSupportedException { Person person=new Person("张三",20,new Address("安徽","合肥")); Person clonePerson=(Person) person.clone(); System.out.println(person); //com.hand.hand.clone.Person@1d81eb93 System.out.println(clonePerson); //com.hand.hand.clone.Person@1d81eb93 System.out.println(person.display()); //Person[name=张三,age=20,addressAdderss[provice=安徽,city=合肥]] System.out.println(clonePerson.display()); //Person[name=张三,age=20,addressAdderss[provice=安徽,city=合肥]]
//证明String的特殊性,因为String是final的,所以会开辟新的内存空间 clonePerson.setName("李四");
//证明基本数据类型是被拷贝是开辟新空间的 clonePerson.setAge(22);
//证明成员变量所指向的是同一个对象 Address address=clonePerson.getAddress(); address.setProvince("江苏"); System.out.println(person.display()); //Person[name=张三,age=20,addressAdderss[provice=江苏,city=合肥]] System.out.println(clonePerson.display()); //Person[name=李四,age=22,addressAdderss[provice=江苏,city=合肥]] } }
四、深拷贝
针对以上成员变量无法完成拷贝的情况出现了深拷贝。
实现方式有以下两种:
- 第一种
与通过重写 clone 方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现 Cloneable 接口并重写 clone 方法,最后在最顶层的类的重写的 clone 方法中调用所有的 clone 方法即可实现深拷贝。简单的说就是:每一层的每个对象都进行浅拷贝 = 深拷贝。
- 第二种
结合序列化来解决这个问题,先把对象序列化,然后再反序列化成对象,该对象保证每个引用都是崭新的。这个就形成了多个引用,原引用和反序列化之后的引用不在相同,具体实现:
public class Student implements Cloneable, Serializable { private static final long serialVersionUID = 1L; private int age; private Address address; public void setAddress(Address address) { this.address = address; } public Address getAddress() { return address; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } @Override protected Object clone() { Student stu = null; try { // 将对象写成byte array ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); // 从流中读出byte array ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); stu = (Student) ois.readObject(); } catch (Exception e) { e.printStackTrace(); } return stu; } } public class Test { public static void main(String[] args) { Student stu1 = new Student(); Address address = new Address(); address.setAddress("beijing"); stu1.setAge(20); stu1.setAddress(address); Student stu2 = (Student) stu1.clone(); address.setAddress("jinan"); stu2.setAge(10); System.out.println(stu1.getAge() + stu1.getAddress().getAddress()); //20jinan System.out.println(stu2.getAge() + stu2.getAddress().getAddress()); //10beijing stu2.setAddress(address); System.out.println(stu1.getAge() + stu1.getAddress().getAddress()); //20jinan System.out.println(stu2.getAge() + stu2.getAddress().getAddress()); //10jinan } }

浙公网安备 33010602011771号