欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

一文深入Java浅拷贝和深拷贝

时间:2023-07-10
值类型和引用类型

理解浅拷贝之前我们需要分清楚值类型(int、float…)、包装类(Integer、Double…)以及自己定义类等类,其实就就是值类型和引用类型两种.
就像上面这个图中,int a=1024是值类型的所以是变量a就是直接等于实际的值1024,而object obj1、object obj2显然是引用类型,obj存储的不是实际的对象,而是对象在堆中的地址。 原型模式-浅拷贝和深拷贝图解

浅拷贝:根据上面来讲解,原型对象在被克隆后克隆出来的新对象和原型对象的地址是不一样的,在克隆出来对象中基础类型的直接复制一份的,也就是说,克隆对象修改值类型和原型对象修改各自的属性是没有半毛钱关系的,而其中的引用对象就不一样了,克隆出来的对象引用地址和原型对象的是同一个,也就说其中任意一个更改引用属性的时候都会影响到对方的属性,都是引用同一个对象。(包装类型比较特殊的引用类型,克隆之后双方都是互不干扰的)。深拷贝:原型对象在通过深拷贝之后,基础类型仍旧是各自独立的,而各自引用对象的地址指向却是不同的,也就说在克隆对象和原型对象有任意一方修改引用参数都不会影响到对方。 先上个简单的demo

public class IdCard { private String cName; public IdCard(String cName) { this.cName = cName; } public String getcName() { return cName; } public void setcName(String cName) { this.cName = cName; } @Override public String toString() { return "IdCard{" + "cName='" + cName + ''' + '}'; }}

public class Student implements Cloneable { private int id; private String name; private IdCard idCard; .....省略get set方法 @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + ''' + ", idCard=" + idCard + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public static void main(String[] args) throws CloneNotSupportedException { IdCard cId = new IdCard("hhhhh"); Student student1 = new Student(12,"student1", cId); Student student2 = (Student) student1.clone(); student2.setId(1); student2.setName("student2"); student2.idCard.setcName("xxxxx"); System.out.println(student1); System.out.println(student2); }}

运行结果如下:
包装类型和值类型的如图可知在浅拷贝后是互不相干的,但是我们自定义的IdCard引用对象一个改了就会影响另一个,这就是由于是同一个对象缘由。 那么为什么我们自定义对象是可变包装类又特殊不可变呢?

其原因是遵守不可变原则,即Immutable设计模式: 类添加final修饰符,保证类不被继承。保证所有成员变量必须私有,并且加上final修饰(不可变指的是引用不可变,也就是不可以重新指向其他对象)不提供改变成员变量的方法,包括setter通过构造器初始化所有成员,进行深拷贝(deep copy)在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝

比如我们随便拿一个包装类Double类分析一下

public final class Double extends Number implements Comparable { public static final double POSITIVE_INFINITY = 1.0 / 0.0; public static final double NEGATIVE_INFINITY = -1.0 / 0.0; public static final double NaN = 0.0d / 0.0; public static final double MAX_VALUE = 0x1.fffffffffffffP+1023; // 1.7976931348623157e+308 public static final double MIN_NORMAL = 0x1.0p-1022; // 2.2250738585072014E-308 @SuppressWarnings("unchecked") public static final Class TYPE = (Class) Class.getPrimitiveClass("double"); public static String toString(double d) { return FloatingDecimal.toJavaFormatString(d); } public static Double valueOf(String s) throws NumberFormatException { return new Double(parseDouble(s)); } public static Double valueOf(double d) { return new Double(d); } public static double parseDouble(String s) throws NumberFormatException { return FloatingDecimal.parseDouble(s); } public static boolean isNaN(double v) { return (v != v); } public static boolean isInfinite(double v) { return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY); } public static boolean isFinite(double d) { return Math.abs(d) <= DoubleConsts.MAX_VALUE; } private final double value; public Double(double value) { this.value = value; } public Double(String s) throws NumberFormatException { value = parseDouble(s); } public boolean isNaN() { return isNaN(value); } public boolean isInfinite() { return isInfinite(value); }

首先这个Double类类是使用final关键字修饰表示不可继承,然后所有成员变量都是使用final修饰表示引用不可变,再其次就是不提供setter方法,即满足不可变原则。 如何浅拷贝

如上面IdCard和Student类就是实现Cloneable接口,并且重写了clone的方法,就可以实现。 如何深拷贝

有两种方法:一种继承Cloneable接口或者序列化继承Cloneable接口

public class IdCard implements Cloneable{ private Double cName; public IdCard(Double cName) { this.cName = cName; } public Double getcName() { return cName; } public void setcName(Double cName) { this.cName = cName; } @Override public String toString() { return "IdCard{" + "cName='" + cName + ''' + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }}

public class Student implements Cloneable { private int id; private String name; private IdCard idCard; public Student(int id, String name, IdCard idCard) { this.id = id; this.name = name; this.idCard = idCard; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public IdCard getIdCard() { return idCard; } public void setIdCard(IdCard idCard) { this.idCard = idCard; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + ''' + ", idCard=" + idCard + '}'; } @Override protected Object clone() throws CloneNotSupportedException { //需要将引用的对象也clone Student student = (Student)super.clone(); student.idCard = (IdCard)idCard.clone(); return student; } public static void main(String[] args) throws CloneNotSupportedException { IdCard cId = new IdCard(12.5); Student student1 = new Student(12,"student1", cId); Student student2 = (Student) student1.clone(); student2.setId(1); student2.setName("student2"); student2.idCard.setcName(10.2); System.out.println(student1); System.out.println(student2); }}

执行结果:
使用序列化和反序列化的方式深度拷贝,和Cloneable相比可以解决多层继承带来的深度克隆失效的问题

public class IdCard implements Serializable { private static final long serialVersionUID = 1L; private Double cName; public IdCard(Double cName) { this.cName = cName; } public Double getcName() { return cName; } public void setcName(Double cName) { this.cName = cName; } @Override public String toString() { return "IdCard{" + "cName='" + cName + ''' + '}'; }}

public class Student implements Serializable { private static final long serialVersionUID = 1L; private int id; private String name; private IdCard idCard; public Student(int id, String name, IdCard idCard) { this.id = id; this.name = name; this.idCard = idCard; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public IdCard getIdCard() { return idCard; } public void setIdCard(IdCard idCard) { this.idCard = idCard; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + ''' + ", idCard=" + idCard + '}'; } public Student deepClone() { Student student = null; try { // 序列化 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this); //反序列化 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); student = (Student) objectInputStream.readObject(); }catch (ClassNotFoundException | IOException e){ e.printStackTrace(); } return student; } public static void main(String[] args) throws CloneNotSupportedException { IdCard cId = new IdCard(12.5); Student student1 = new Student(12,"student1", cId); Student student2 = (Student) student1.deepClone(); student2.setId(1); student2.setName("student2"); student2.idCard.setcName(10.2); System.out.println(student1); System.out.println(student2); }}

执行结果:
集合浅拷贝和深拷贝 linkedList浅拷贝

通过构造方式

IdCard ggc = new IdCard(1, "ggc"); List list1 = new linkedList<>(); list1.add(ggc); List list2 = new linkedList<>(list1); list2.get(0).setName("mm"); System.out.println(list1); System.out.println(list2);

执行结果:
通过forEach一个个插入

IdCard ggc = new IdCard(1, "ggc"); List list1 = new linkedList<>(); list1.add(ggc); List list2 = new linkedList<>(); list1.forEach(s->list2.add(s)); list2.get(0).setName("mm"); System.out.println(list1); System.out.println(list2);

执行结果:
数组通过System.arraycopy

IdCard ggc = new IdCard(1, "ggc"); IdCard[] arr1 = new IdCard[]{ggc}; IdCard[] arr2 = new IdCard[1]; System.arraycopy(arr1,0,arr2,0,arr1.length); arr2[0].setName("hhhh"); Arrays.asList(arr1).forEach(System.out::println); Arrays.asList(arr2).forEach(System.out::println);

执行结果
linkedList深拷贝

通过list自带的addAll()方法

List list1 = new linkedList<>(Arrays.asList(3,5,2,1)); List list2 = new linkedList<>(); list2.addAll(list1); list2.remove(0); System.out.println(list1); System.out.println(list2);

执行结果:
通过序列化和反向序列化

public class IdCard implements Serializable { private static final long serialVersionUID = 1L; private int Id; private String name; public IdCard(int id, String name) { Id = id; this.name = name; } public int getId() { return Id; } public void setId(int id) { Id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "IdCard{" + "Id=" + Id + ", name='" + name + ''' + '}'; }}

public static List deepClone(List source) throws IOException, ClassNotFoundException { //序列化 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(source); //反向序列化 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream); List o = (List)inputStream.readObject(); return o;}

public static void main(String[] args) throws IOException, ClassNotFoundException { List list1 = new linkedList<>(); list1.add(new IdCard(1,"123")); list1.add(new IdCard(2,"1233")); List list2 = deepClone(list1); list2.remove(0); System.out.println(list1); System.out.println(list2);}

执行结果:
实现Cloneable接口,重写clone方法

@Override public Object clone(){ IdCard clone =null; try { clone = (IdCard) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; }

IdCard idCard = new IdCard(1, "213"); IdCard idCard2 = (IdCard) idCard.clone(); idCard2.setName("gccc"); System.out.println(idCard); System.out.println(idCard2);

执行结果:

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。