java浅拷贝与深拷贝原理剖析

介绍

在开发过程中,经常会遇到将一个对象的所有成员拷贝给另一个对象的需求。

普通的对象形式一般如下:

定义一个普通Student类:

public class Student {
  private String name;

  private Integer age;

  private Integer totalScore;
  
  //省略get set
  ...
  
  @Override
  public String toString() {
    return "Student{" +
        "name='" + name + '\'' +
        ", age=" + age +
        ", totalScore=" + totalScore +
        ", classs=" + classs +
        '}';
  }
}
  public static void main(String[] args) {
  	Student stu = new Student();
    stu.setName("张三");
    stu.setAge(18);
    Student stu1 = stu;
    stu1.setName("李四");
    sout(stu.equals(stu1));
    sout(stu1.getName());
  }

以上程序运行结果:

true
李四

由以上结果得出结论:普通对象拷贝没有生成新的对象,二者对象在内存中地址是一样的,这种情形下就会存在一个问题,当修改其中一个对象(这里其实应该叫引用)的值,另一个对象的值也会改变。内存模型图如下:
在这里插入图片描述

由上面的问题引出了浅拷贝与深拷贝,根据对对象的拷贝程度分为:

  • 浅拷贝
  • 深拷贝
浅拷贝
概念

浅拷贝会创建一个新的对象,这个对象是对原对象属性值的完整的拷贝,如果是基本类型拷贝的是基本类型,如果是引用,拷贝的是引用指向的对象的内存地址;所以当拷贝对象修改引用的属性时,原对象的引用的属性值也会发生改变

实现

要实现浅拷贝的类,需要实现Cloneable接口,覆盖clone()方法

public class Student implements Cloneable{
private String name;

  private Integer age;

  private Integer totalScore;

  private Classs classs;

  public Student() {
  }

  public Student(String name, Integer age, Integer totalScore, Classs classs) {
    this.name = name;
    this.age = age;
    this.totalScore = totalScore;
    this.classs = classs;
  }

  //省略get set方法

  @Override
  protected Object clone() {
    //浅拷贝直接调用父类的clone方法
    return super.clone();
  }

  @Override
  public String toString() {
    return "Student{" +
        "name='" + name + '\'' +
        ", age=" + age +
        ", totalScore=" + totalScore +
        ", classs=" + classs +
        '}';
  } 
}
public class Classs{

  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Classs() {
  }

  public Classs(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "Classs{" +
        "name='" + name + '\'' +
        '}';
  }
}

main方法测试并输出结果

 public static void main(String[] args) throws CloneNotSupportedException {
 	Classs classs = new Classs("三年2班");
    Student stu = new Student("小明",18,666,classs);

    Student stu1 = (Student) stu.clone();
    stu1.setName("小芳");
    stu1.setAge(17);
    stu1.setTotalScore(691);
    Classs classs1 = stu.getClasss();
    classs1.setName("四年1班");

    System.out.println(student.toString());
    System.out.println(student1.toString());
 }

打印结果:

Student{name='小芳', age=18, totalScore=666, classs=Classs{name='四年1班'}}
Student{name='小明', age=17, totalScore=691, classs=Classs{name='四年1班'}}

由打印结果可知:当stu调用clone方法实现拷贝对象,再对拷贝对象普通属性进行重新赋值可以发现两个对象的属性值都不相同,而引用属性classs的name属性,在改变之后,两个对象的引用属性值都发生了改变。
在这里插入图片描述

深拷贝
概念

其实就是在浅拷贝的基础上,使得引用属性的类拷贝时也在内存中开辟对立的空间存放拷贝的引用属性对象,保证在修改拷贝对象或原对象的属性的前提下不会影响另一个。

实现

对于引用类型的成员变量,其类的内部也要需要实现 Cloneable 并重写 clone() 方法,如果有多层对象嵌套的,每个对象都需要实现 Cloneable 并重写 clone() 方法。

public class Student implements Cloneable{

  private String name;

  private Integer age;

  private Integer totalScore;

  private Classs classs;

  public Student() {
  }

  public Student(String name, Integer age, Integer totalScore, Classs classs) {
    this.name = name;
    this.age = age;
    this.totalScore = totalScore;
    this.classs = classs;
  }
//省略get set方法

  @Override
  protected Object clone() {
    //深拷贝需要对引用属性创建新的对象并分配内存,
    try {
      Student student = (Student) super.clone();
      student.classs = (Classs) classs.clone();
      return student;
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
      return null;
    }
  }

  @Override
  public String toString() {
    return "Student{" +
        "name='" + name + '\'' +
        ", age=" + age +
        ", totalScore=" + totalScore +
        ", classs=" + classs +
        '}';
  }
}
public class Classs implements Cloneable{

  private String name;

  //省略get set方法
    
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }

  @Override
  public String toString() {
    return "Classs{" +
        "name='" + name + '\'' +
        '}';
  }
}

main方法测试

public static void main(String []args){
    Classs classs = new Classs("三年2班");
    Student student = new Student("小明",18,666,classs);

    Student student1 = (Student) student.clone();
    student1.setName("小芳");
    student1.setAge(17);
    student1.setTotalScore(691);
    Classs classs1 = student.getClasss();
    classs1.setName("四年1班");

    System.out.println(student.toString());
    System.out.println(student1.toString());
}

测试打印结果如下:

 Student{name='小明', age=18, totalScore=666, classs=Classs{name='四年1班'}}
 Student{name='小芳', age=17, totalScore=691, classs=Classs{name='三年2班'}}

由输出结果可知,两个对象之间的成员属性发生改变之后,并没有互相影响。
在这里插入图片描述


总结

深拷贝相较于浅拷贝,主要的是为引用数据类型创建了新的内存空间,
使两个不同的对象的引用数据指向了不同的内存地址,在改变其引用数据的属性时,不会引起连锁反应对另一个产生影响。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值