一、直接赋值

直接赋值的方式没有生产新的对象,只是拷贝了对象引用地址而已。

示例:

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 中的浅克隆与深克隆相关推荐

  1. Java中的浅克隆与深克隆

    Java中的浅克隆与深克隆 一:前言 二:浅克隆与深克隆的区别 一:前言 克隆,即复制一个对象,该对象的属性与被复制的对象一致,如果不使用Object类中的clone方法实现克隆,可以自己new出一个 ...

  2. 详细分析Java中的浅克隆和深克隆

    本文对浅克隆和深克隆的两种方法(不引入别的开源工具)进行了简单的代码实现(没有内部类语法),对比了浅克隆和深克隆对引用类型的影响,暂不考虑不可变类,确保初学Java者能够看懂并学会,可直接复制源代码进 ...

  3. Java对象克隆——浅克隆和深克隆的区别

    在Java中对象的克隆有深克隆和浅克隆之分.有这种区分的原因是Java中分为基本数据类型和引用数据类型,对于不同的数据类型在内存中的存储的区域是不同的.基本数据类型存储在栈中,引用数据类型存储在堆中. ...

  4. 细说Java中的字符和字符串(一)

    #一道经典问题 Java里的char类型能不能存储一个中文字符? 对于这道题,绝大多数的答案都是"可以存储".给出的原因包括: java中的char是unicode存储,unico ...

  5. java中的单例_细说Java中的几种单例模式

    在Java中,单例模式分为很多种,本人所了解的单例模式有以下几种,如有不全还请大家留言指点: 饿汉式 懒汉式/Double check(双重检索) 静态内部类 枚举单例 一.饿汉式 image 饿汉式 ...

  6. java对象的浅克隆和深克隆

    引言: 在Object基类中,有一个方法叫clone,产生一个前期对象的克隆,克隆对象是原对象的拷贝,由于引用类型的存在,有深克隆和浅克隆之分,若克隆对象中存在引用类型的属性,深克隆会将此属性完全拷贝 ...

  7. 标记注解 java_【java】细说 JAVA中 标注 注解(annotation)

    Java注解是附加在代码中的一些元信息,用于一些工具在编译.运行时进行解析和使用,起到说明.配置的功能. 注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用 下面我们来详细说说这个注解,到底是怎么 ...

  8. Java中的深克隆和浅克隆的原理及三种方式实现深克隆

      本文详细介绍了Java中的浅克隆和深克隆的概念,及案例演示如何实现深克隆! 文章目录 1 克隆概述 2 深克隆实现 3 案例 3.1 测试普通clone方法--浅克隆 3.2 使用重写后的clon ...

  9. 浅析Java中的深克隆和浅克隆

    说实话,目前为止还没在项目中遇到过关于Java深克隆和浅克隆的场景.今天手抖戳开了花呗账单,双十二败家的战绩真是惨不忍睹,若能在我的客户端"篡改"下账单金额,那该(简)有(止)多( ...

  10. 【重难点】【Java基础 06】浅克隆与深克隆、Object类的常用方法、util包下的接口

    [重难点][Java基础 06]浅克隆与深克隆.Object对象的常用方法.util包下的接口 文章目录 [重难点][Java基础 06]浅克隆与深克隆.Object对象的常用方法.util包下的接口 ...

最新文章

  1. tornado 异步两种实现形式 通过回调可以利用
  2. 马斯克矩阵模拟错了?这个试验证明人类不是「缸中之脑」
  3. 关于更改MYECLIPSE JS 代码背景颜色
  4. 输出值(4)输出值的应用
  5. 【前端模块】HTML5标签
  6. IntelliJ IDEA:使用Google Guava生成equals,hashCode和toString
  7. Confluence 6 自动添加用户到用户组
  8. leetcode230. 二叉搜索树中第K小的元素(中序遍历)
  9. 技术员例会记要(一)
  10. ElasticSearch概述(一)——简介
  11. GEE开发之Landsat8计算NDWI和数据分析
  12. Tableau零基础教程
  13. R语言中dim函数_R语言在医学统计中的应用基础教程
  14. 好用的函数在线绘图工具
  15. 哪个版本的linux适合个人主机,2020年适合个人使用的Linux发行版推荐TOP5
  16. 哪有什么高效安全运行,只不过是磁盘之间在负重前行 Linux RAID磁盘阵列
  17. view \function\行转列的方法。
  18. 关于 simulink 的 1/z 模块是什么的问题
  19. javascript 闭包_了解JavaScript闭包:实用方法
  20. 三星java世界x108_我对三星X100/X108的使用感受

热门文章

  1. Python计算思维训练——数组和曲线绘制练习(三)
  2. fantastic组需求分析
  3. python实例013--定义一个矩形类
  4. 人类的行为与程序计算
  5. Eclipse 更换皮肤
  6. 现在的男生为什么不主动追求女生了
  7. 地图实时定位我的位置
  8. NetWorkHelper 检测网络状态
  9. CSDN 技术问答升级规则
  10. 深入解析SQL Server 2008