细说 Java 中的浅克隆与深克隆
一、直接赋值
直接赋值的方式没有生产新的对象,只是拷贝了对象引用地址而已。
示例:
public class CloneTest {public static void main(String[] args) {// 初始化对象Person person = new Person("张三", 18);// 克隆对象Person personClone= person;// 改变克隆对象的属性值personClone.setName("李四");personClone.setAge(20);// 分别打印初始对象和克隆对象System.out.println(person);System.out.println(personClone);}
}@Getter
@Setter
@ToString
@AllArgsConstructor
class Person {private String name; // 姓名private int age; // 年龄
}
输出结果:
Person(name=李四, age=20)
Person(name=李四, age=20)
我们将 person 对象复制给了 personClone 对象,然后对 personClone 对象的 name 属性和 age 属性进行了修改,并未修改 person 对象的属性值,但是我们最后发现 person 对象的 name 属性和 age 属性也发生了变化,所以这个结果证明了我们上面的结论。
直接赋值在 Java 内存中的模型大概是这样的:
二、浅克隆
在浅克隆中,当对象被复制时只克隆它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有克隆。
要实现对象浅克隆还是比较简单的,只需要被复制类需要实现 Cloneable 接口,重写 clone 方法即可,对 person 类进行改造,使其可以支持浅克隆。
示例:
public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException {// 初始化对象Person person = new Person("张三", 18, new Pet("小白"));// 克隆对象Person personClone = (Person) person.clone();// 改变克隆对象属性值(值类型)personClone.setName("李四");personClone.setAge(20);// 改变克隆对象属性值(引用类型)personClone.getPet().setName("小黄");// 分别打印初始对象和克隆对象System.out.println(person);System.out.println(personClone);}
}@Getter
@Setter
@ToString
@AllArgsConstructor
class Person implements Cloneable {private String name; // 姓名private int age; // 年龄private Pet pet; // 宠物/*** 重写 clone() 方法*/@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}@Getter
@Setter
@ToString
@AllArgsConstructor
class Pet implements Cloneable {private String name; // 姓名/*** 重写 clone() 方法*/@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
输出结果:
Person(name=张三, age=18, pet=Pet(name=小黄))
Person(name=李四, age=20, pet=Pet(name=小黄))
在这里我们修改了 personClone 对象的 name 属性值,personClone 对象的 name 字段指向了内存中新的 name 对象,但是我们并没有改变 person 对象的 name 字段的指向,所以 person 对象的 name 还是指向内存中原来的 name 地址,也就没有变化,age 属性同理。
我们修改 personClone 的 pet 字段之后,person 的 pet 也发生了改变,这说明 person 对象和 personClone 对象指向是同一个 pet 对象地址,这也符合浅克隆引用对象只克隆引用地址并未创建新对象的定义。
浅克隆在 Java 内存中的模型大概是这样的:
三、深克隆
深克隆是一种完全克隆,无论是值类型还是引用类型都会完完全全的克隆一份,在内存中生成一个新的对象。
深克隆有两种方式,一种是跟浅克隆一样实现 Cloneable 接口,另一种是实现 Serializable 接口,用序列化的方式来实现深克隆,我们分别用这两种方式来实现深克隆。
3.1 实现 Cloneable 接口方式
实现 Cloneable 接口的方式跟浅克隆相差不大,我们需要引用对象也实现 Cloneable 接口,具体代码改造如下:
public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException {// 初始化对象Person person = new Person("张三", 18, new Pet("小白"));// 克隆对象Person personClone = (Person) person.clone();// 改变克隆对象属性值(值类型)personClone.setName("李四");personClone.setAge(20);// 改变克隆对象属性值(引用类型)personClone.getPet().setName("小黄");// 分别打印初始对象和克隆对象System.out.println(person);System.out.println(personClone);}
}@Getter
@Setter
@ToString
@AllArgsConstructor
class Person implements Cloneable {private String name; // 姓名private int age; // 年龄private Pet pet; // 宠物/*** 重写 clone() 方法*/@Overrideprotected Object clone() throws CloneNotSupportedException {Person person = (Person) super.clone();// 需要将引用对象也克隆一次person.pet = (Pet) pet.clone();return person;}
}@Getter
@Setter
@ToString
@AllArgsConstructor
class Pet implements Cloneable {private String name; // 姓名/*** 重写 clone() 方法*/@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
输出结果:
Person(name=张三, age=18, pet=Pet(name=小白))
Person(name=李四, age=20, pet=Pet(name=小黄))
可以看出,修改 personClone 的 pet 时,对 person 的 pet 已经没有影响了,说明进行了深克隆,在内存中重新生成了一个新的对象。
深克隆在 Java 内存中的模型大概是这样的:
3.2 实现 Serializable 接口方式
实现 Serializable 接口方式也可以实现深拷贝,而且这种方式还可以解决多层克隆的问题。
多层克隆就是引用类型里面又有引用类型,层层嵌套下去,用 Cloneable 方式实现还是比较麻烦的,一不小心写错了就不能实现深拷贝了。
使用 Serializable 序列化的方式就需要所有的对象对实现 Serializable 接口,我们对代码进行改造,改造成序列化的方式:
public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException {// 初始化对象Person person = new Person("张三", 18, new Pet("小白"));// 克隆对象Person personClone = (Person) person.clone();// 改变克隆对象属性值(值类型)personClone.setName("李四");personClone.setAge(20);// 改变克隆对象属性值(引用类型)personClone.getPet().setName("小黄");// 分别打印初始对象和克隆对象System.out.println(person);System.out.println(personClone);}
}@Getter
@Setter
@ToString
@AllArgsConstructor
class Person implements Serializable {private static final long serialVersionUID = 1L;private String name; // 姓名private int age; // 年龄private Pet pet; // 宠物public Person clone() {Person person = null;try { // 将该对象序列化成流(因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,所以利用这个特性可以实现对象的深克隆)ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(this);// 将流序列化成对象ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);person = (Person) ois.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}return person;}
}@Getter
@Setter
@ToString
@AllArgsConstructor
class Pet implements Serializable {private String name; // 姓名
}
输出结果:
Person(name=张三, age=18, pet=Pet(name=小白))
Person(name=李四, age=20, pet=Pet(name=小黄))
运行 main 方法,我们可以得到跟 Cloneable 方式一样的结果,序列化的方式也实现了深拷贝。
到此关于 Java 浅克隆和深克隆的相关内容就介绍完了,希望你有所收获。
写在最后:
扫描下方二维码或关注微信公众号:CodeFish 回复关键字 “电子书” 即可获取相关书籍资料!
更多优质电子书资源持续更新中…
细说 Java 中的浅克隆与深克隆相关推荐
- Java中的浅克隆与深克隆
Java中的浅克隆与深克隆 一:前言 二:浅克隆与深克隆的区别 一:前言 克隆,即复制一个对象,该对象的属性与被复制的对象一致,如果不使用Object类中的clone方法实现克隆,可以自己new出一个 ...
- 详细分析Java中的浅克隆和深克隆
本文对浅克隆和深克隆的两种方法(不引入别的开源工具)进行了简单的代码实现(没有内部类语法),对比了浅克隆和深克隆对引用类型的影响,暂不考虑不可变类,确保初学Java者能够看懂并学会,可直接复制源代码进 ...
- Java对象克隆——浅克隆和深克隆的区别
在Java中对象的克隆有深克隆和浅克隆之分.有这种区分的原因是Java中分为基本数据类型和引用数据类型,对于不同的数据类型在内存中的存储的区域是不同的.基本数据类型存储在栈中,引用数据类型存储在堆中. ...
- 细说Java中的字符和字符串(一)
#一道经典问题 Java里的char类型能不能存储一个中文字符? 对于这道题,绝大多数的答案都是"可以存储".给出的原因包括: java中的char是unicode存储,unico ...
- java中的单例_细说Java中的几种单例模式
在Java中,单例模式分为很多种,本人所了解的单例模式有以下几种,如有不全还请大家留言指点: 饿汉式 懒汉式/Double check(双重检索) 静态内部类 枚举单例 一.饿汉式 image 饿汉式 ...
- java对象的浅克隆和深克隆
引言: 在Object基类中,有一个方法叫clone,产生一个前期对象的克隆,克隆对象是原对象的拷贝,由于引用类型的存在,有深克隆和浅克隆之分,若克隆对象中存在引用类型的属性,深克隆会将此属性完全拷贝 ...
- 标记注解 java_【java】细说 JAVA中 标注 注解(annotation)
Java注解是附加在代码中的一些元信息,用于一些工具在编译.运行时进行解析和使用,起到说明.配置的功能. 注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用 下面我们来详细说说这个注解,到底是怎么 ...
- Java中的深克隆和浅克隆的原理及三种方式实现深克隆
本文详细介绍了Java中的浅克隆和深克隆的概念,及案例演示如何实现深克隆! 文章目录 1 克隆概述 2 深克隆实现 3 案例 3.1 测试普通clone方法--浅克隆 3.2 使用重写后的clon ...
- 浅析Java中的深克隆和浅克隆
说实话,目前为止还没在项目中遇到过关于Java深克隆和浅克隆的场景.今天手抖戳开了花呗账单,双十二败家的战绩真是惨不忍睹,若能在我的客户端"篡改"下账单金额,那该(简)有(止)多( ...
- 【重难点】【Java基础 06】浅克隆与深克隆、Object类的常用方法、util包下的接口
[重难点][Java基础 06]浅克隆与深克隆.Object对象的常用方法.util包下的接口 文章目录 [重难点][Java基础 06]浅克隆与深克隆.Object对象的常用方法.util包下的接口 ...
最新文章
- tornado 异步两种实现形式 通过回调可以利用
- 马斯克矩阵模拟错了?这个试验证明人类不是「缸中之脑」
- 关于更改MYECLIPSE JS 代码背景颜色
- 输出值(4)输出值的应用
- 【前端模块】HTML5标签
- IntelliJ IDEA:使用Google Guava生成equals,hashCode和toString
- Confluence 6 自动添加用户到用户组
- leetcode230. 二叉搜索树中第K小的元素(中序遍历)
- 技术员例会记要(一)
- ElasticSearch概述(一)——简介
- GEE开发之Landsat8计算NDWI和数据分析
- Tableau零基础教程
- R语言中dim函数_R语言在医学统计中的应用基础教程
- 好用的函数在线绘图工具
- 哪个版本的linux适合个人主机,2020年适合个人使用的Linux发行版推荐TOP5
- 哪有什么高效安全运行,只不过是磁盘之间在负重前行 Linux RAID磁盘阵列
- view \function\行转列的方法。
- 关于 simulink 的 1/z 模块是什么的问题
- javascript 闭包_了解JavaScript闭包:实用方法
- 三星java世界x108_我对三星X100/X108的使用感受