谈谈java中对象的深拷贝与浅拷贝

时间:2022-09-06 20:42:20

知识点:java中关于Object.clone方法,对象的深拷贝与浅拷贝

引言

在一些场景中,我们需要获取到一个对象的拷贝,这时候就可以用java中的Object.clone方法进行对象的复制,得到一个一模一样的新对象。(使用new再次创建一个相同的对象也是可以的,有些区别),当对象中含有可变的引用类型属性时,在复制得到的新对象对该引用类型属性内容进行修改,原始对象相应的属性内容也会发生变化,这就是"浅拷贝"的现象。反之原始对象相应的引用类型属性不发生变化,是 "深拷贝"。

浅拷贝

先看一个浅拷贝的demo实例

1.创建一个Person类

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;
}
} 由于clone()方法是protected修饰的,因此需要实现Cloneable接口才能调用,同时需要覆写clone()方法才能调用。 2.创建一个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+"]";
}
} 3.浅拷贝测试
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);
System.out.println(clonePerson); System.out.println(person.display());
System.out.println(clonePerson.display()); clonePerson.setName("李四");
clonePerson.setAge(22);
Address address=clonePerson.getAddress();
address.setProvince("江苏"); System.out.println(person.display());
System.out.println(clonePerson.display());
}
} 4.代码运行结果:
谈谈java中对象的深拷贝与浅拷贝

第1、2句输出说明了原对象与新对象是两个不同的对象。

第3、4句可以看到拷贝出来的新对象与原对象内容一致

但是,接着将新对象里面的信息进行了修改,然后输出发现原对象里面的部分信息也跟着变了。仔细观察发现原对象跟着变化的只是Address部分,这就跟clone本身的浅拷贝有关系了。

浅拷贝  :创建一个新对象,然后将当前对象的非静态字段复制到该对象,如果字段类型是值类型(基本类型)的,那么对该字段进行复制;如果字段是引用类型的,则只复制该字段的引用而不复制引用指向的对象。此时新对象里面的引用类型字段相当于是原始对象里面引用类型字段的一个副本,原始对象与新对象里面的引用字段指向的是同一个对象。

简而言之:类实现默认的Object.clone()方法,拷贝对象时,对于引用类型的成员变量(属性)拷贝只是拷贝“值”即地址(引用),没有在堆中开辟新的内存空间。

因此,修改clonePerson里面的address内容时,原person里面的address内容会跟着改变。

深拷贝” :类重写clone()方法,对于引用类型成员变量,重新在堆中开辟新的内存空间,简单地说,将引用类型的属性内容也拷贝一份新的。

如果我们想实现深拷贝,有两种方法,第一种是给需要拷贝的引用类型也实现Cloneable接口并覆写clone方法;第二种则是利用序列化。

具体代码演示可参考:https://www.cnblogs.com/nickhan/p/8569329.html

接下来补充一下,涉及到的知识点

复制对象和复制引用的区别

谈谈java中对象的深拷贝与浅拷贝

谈谈java中对象的深拷贝与浅拷贝

 
谈谈java中对象的深拷贝与浅拷贝

谈谈java中对象的深拷贝与浅拷贝

谈谈java中对象的深拷贝与浅拷贝

String的特殊性性

其实string类型也是引用类型,不是基本类型,那上面的例子中,修改拷贝对象person的name后,原对象的name值并没有改变,这是什么原因呢?

答:其实string也是引用类型,在深复制时并没有进行单独复制,仅仅复制了引用,String并没有实现cloneable接口,之所以修改新克隆对象的name值后,

原对象的name对应的值没有改变,是因为String在内存中是不可以改变的对象,我们在修改新对象(或者原对象)的name值时,会新分配一块内存保存新修改的name值,新对象的引用(或者原对象的引用)会指向新的内存空间,原来的String 还会存在它的引用name,如果String的值不存在引用,会被回收,也因为原来的String值存在引用name,所以不会被回收,这样,可以解释上面的demo中,name虽然是String引用类型,但是修改name的值,并没有改变原对象的name值。

我们也可以在idea中,用debugger调试验证一下

谈谈java中对象的深拷贝与浅拷贝

执行上面代码前三行代码,debugger

谈谈java中对象的深拷贝与浅拷贝

上面的箭头可以看出person和clonePerson的name是指向同一对象的引用(@468)

修改其中的对象的name值debugger看看

谈谈java中对象的深拷贝与浅拷贝

当我们执行clonePerson.setName("李四");修改新克隆对象的name值以后,则:

新克隆对象的name引用对应的值,发生了改变,原对象的引用对应的内存值没有发生变化

克隆一个对象与new一个对象的区别

(1)java中clone和new都可以创建一个新的对象

(2)clone()不会调用构造方法,而new一个对象会调用类的构造方法

(3)clone()能快速创建一个已有对象的副本,即创建对象并且将已有对象中所有属性值克隆;new只能在JVM中申请一个空的内存区域,对象的属性值要通过构造方法赋值。

谈谈java中对象的深拷贝与浅拷贝的更多相关文章

  1. PHP中对象的深拷贝与浅拷贝

    先说一下深拷贝和浅拷贝通俗理解 深拷贝:赋值时值完全复制,完全的copy,对其中一个作出改变,不会影响另一个 浅拷贝:赋值时,引用赋值,相当于取了一个别名.对其中一个修改,会影响另一个 PHP中, = ...

  2. 探究JS中对象的深拷贝和浅拷贝

    深拷贝和浅拷贝的区别 在讲深拷贝和浅拷贝的区别之前,回想一下我们平时拷贝一个对象时是怎么操作的?是不是像这样? var testObj1 = {a: 1, b:2}, testObj2=testObj ...

  3. java 复制Map对象(深拷贝与浅拷贝)

      java 复制Map对象(深拷贝与浅拷贝) CreationTime--2018年6月4日10点00分 Author:Marydon 1.深拷贝与浅拷贝 浅拷贝:只复制对象的引用,两个引用仍然指向 ...

  4. 谈谈JAVA中的安全发布

    谈谈JAVA中的安全发布 昨天看到一篇文章阐述技术类资料的"等级",看完之后很有共鸣.再加上最近在工作中越发觉得线程安全性的重要性和难以捉摸,又掏出了<Java并发编程实战& ...

  5. 谈谈java中静态变量与静态方法在有继承关系的两个类中调用

    谈谈java中静态变量与静态方法在有继承关系的两个类中调用 学习的中如果遇到不明白或者不清楚的的时候,就是自己做些测试,自己去试试,这次我就做一个关于静态变量和静态方法在有继承关系的两个类中的问题测试 ...

  6. Java中对象的深复制和浅复制详解

    1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵ ...

  7. java 中对象比较大小

    java 中对象比较大小 java 中对象比较大小有两种方法 1:实现Comparable 接口 的 public int compareTo(T o) 方法: 2:实现Comparator 接口 的 ...

  8. Java中对象、对象引用、堆、栈、值传递以及引用传递的详解

    Java中对象.对象引用.堆.栈.值传递以及引用传递的详解 1.对象和对象引用的差别: (1).对象: 万物皆对象.对象是类的实例. 在Java中new是用来在堆上创建对象用的. 一个对象能够被多个引 ...

  9. 谈谈java中成员变量与成员方法继承的问题

    谈谈java中成员变量与成员方法继承的问题 关于成员变量和成员方法的的继承问题,我也可以做一个小测试,来看看结果. 首先我们先创建一个父类:

随机推荐

  1. OSG计时器与时间戳

    static osg::Timer* sendMsgTimer = new osg::Timer; if (sendMsgTimer->time_m()>100)//100ms {// d ...

  2. C&plus;&plus;输入cout与输出cin

    输入和输出并不是C++语言中的正式组成成分.C和C++本身都没有为输入和输出提供专门的语句结构.输入输出不是由C++本身定义的,而是在编译系统提供的I/O库中定义的.C++的输出和输入是用" ...

  3. 用sql查询当天&comma;一周&comma;一个月的数据

    用sql查询当天,一周,一个月的数据   数据查询,不管在网站还是在系统,都很常见,下文是介绍最常见的以日期查询的语句 select * from ShopOrder where datediff(w ...

  4. Android如何缩减APK包大小

    代码 保持良好的编程习惯,不要重复或者不用的代码,谨慎添加libs,移除使用不到的libs. 使用proguard混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安装包的大小. native c ...

  5. 泛型容器单元&lpar;Generics&period;Collections&rpar;&lbrack;3&rsqb;&colon; TStack&lt&semi;T&gt&semi; 堆栈列表

    TQueue 和 TStack, 一个是队列列表, 一个是堆栈列表; 一个是先进先出, 一个是先进后出. TStack 主要有三个方法.一个属性:Push(压栈).Pop(出栈).Peek(查看下一个 ...

  6. 使用DateDiff方法获取日期时间的间隔数

    一:用DateDiff方法获取日期时间的间隔数,截图 二:代码 using System; using System.Windows.Forms; using Microsoft.VisualBasi ...

  7. noi 7219&colon;复杂的整数划分问题

    7219:复杂的整数划分问题 查看 提交 统计 提问 总时间限制:  200ms 内存限制:  65536kB 描述 将正整数n 表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>= ...

  8. Extjs6(特别篇)——项目自带例子main&period;js拆分详解

    本文基于extjs6.0.0 一.拆分代码来看看 1.主页面main是个tab页: 写一些页面的依赖: 标明页面的controller和viewModel Ext.define('Learning.v ...

  9. 【原创】数据库基础之Mysql(1)常用命令

    1 创建用户 CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 比如 create user 'test_user'@'%' identi ...

  10. linq中分组查询而且获取每个分组中的第一条记录,数据用于分页绑定

    LINQ分组取出第一条数据 Person1: Id=1, Name="Test1" Person2: Id=1, Name="Test1" Person3: I ...