引言:

在Object基类中,有一个方法叫clone,产生一个前期对象的克隆,克隆对象是原对象的拷贝,由于引用类型的存在,有深克隆和浅克隆之分,若克隆对象中存在引用类型的属性,深克隆会将此属性完全拷贝一份,而浅克隆仅仅是拷贝一份此属性的引用。首先看一下容易犯的几个小问题

  • clone方法是Object类的,并不是Cloneable接口的,Cloneable只是一个标记接口,标记接口是用用户标记实现该接口的类具有某种该接口标记的功能,常见的标记接口有三个:Serializable、Cloneable、RandomAccess,没有实现Cloneable接口,那么调用clone方法就会爆出CloneNotSupportedException异常。

  • Object类中的clone方法是protected修饰的,这就表明我们在子类中不重写此方法,就在子类外无法访问,因为这个protected权限是仅仅能在Object所在的包和子类能访问的,这也验证了子类重写父类方法权限修饰符可以变大但不能变小的说法。

    protected native Object clone() throws CloneNotSupportedException;
    
  • 重写clone方法,内部仅仅是调用了父类的clone方法,其实是为了扩大访问权限,当然你可以把protected改为public,以后再继承就不用重写了。当然只是浅克隆的clone函数,深克隆就需要修改了。

    @Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
    
  • 属性是String的情况,String也是一个类,那String引用类型吗?String的表现有的像基本类型,归根到底就是因为String不可改变,克隆之后俩个引用指向同一个String,但当修改其中的一个,改的不是String的值,却是新生成一个字符串,让被修改的引用指向新的字符串。外表看起来就像基本类型一样。

浅克隆:

浅克隆就是引用类型的属性无法完全复制,类User中包含成绩属性Mark,Mark是由Chinese和math等等组成的,浅克隆失败的例子

class Mark{private int chinese;private int math;public Mark(int chinese, int math) {this.chinese = chinese;this.math = math;}public void setChinese(int chinese) {this.chinese = chinese;}public void setMath(int math) {this.math = math;}@Overridepublic String toString() {return "Mark{" +"chinese=" + chinese +", math=" + math +'}';}
}
public class User implements Cloneable{private String name;private int age;private Mark mark;public User(String name, int age,Mark mark) {this.name = name;this.age = age;this.mark = mark;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", mark=" + mark +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}public static void main(String[] args) throws CloneNotSupportedException {Mark mark = new Mark(100,99);User user = new User("user",22,mark);User userClone = (User) user.clone();System.out.println("原user:"+user);System.out.println("克隆的user:"+userClone);//修改引用类型的mark属性user.mark.setMath(60);System.out.println("修改后的原user:"+user);System.out.println("修改后的克隆user:"+userClone);}
}

输出结果为:

原user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=99}}
    克隆的user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=99}}
    修改后的原user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=60}}
    修改后的克隆user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=60}}

很清楚的看到user的mark更改后,被克隆的user也修改了。而要想不被影响,就需要深克隆了。

深克隆:

深克隆有两种方式:clone方法的嵌套调用、序列化和反序列化

方式一:clone函数的嵌套调用

既然引用类型无法被完全克隆,那将引用类型也实现Cloneable接口重写clone方法,在User类中的clone方法调用属性的克隆方法,也就是方法的嵌套调用

class Mark implements Cloneable{private int chinese;private int math;public Mark(int chinese, int math) {this.chinese = chinese;this.math = math;}public void setChinese(int chinese) {this.chinese = chinese;}public void setMath(int math) {this.math = math;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Mark{" +"chinese=" + chinese +", math=" + math +'}';}
}
public class User implements Cloneable{private String name;private int age;private Mark mark;public User(String name, int age,Mark mark) {this.name = name;this.age = age;this.mark = mark;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", mark=" + mark +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {User user = (User) super.clone();user.mark = (Mark) this.mark.clone();return user;}public static void main(String[] args) throws CloneNotSupportedException {Mark mark = new Mark(100,99);User user = new User("user",22,mark);User userClone = (User) user.clone();System.out.println("原user:"+user);System.out.println("克隆的user:"+userClone);//修改引用类型的mark属性user.mark.setMath(60);System.out.println("修改后的原user:"+user);System.out.println("修改后的克隆user:"+userClone);}
}

输出结果为:

原user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=99}}
    克隆的user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=99}}
    修改后的原user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=60}}
    修改后的克隆user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=99}}

方式二:序列化和反序列化

上一种方法已经足够满足我们的需要,但是如果类之间的关系很多,或者是有的属性是数组呢,数组可无法实现Cloneable接口(我们可以在clone方法中手动复制数组),但是每次都得手写clone方法,很麻烦,而序列化方式只需要给每个类都实现一个Serializable接口,也是标记接口,最后同序列化和反序列化操作达到克隆的目的(包括数组的复制)。序列化和反序列化的知识请参照下一篇

import java.io.*;
class Mark implements Serializable {private int chinese;private int math;public Mark(int chinese, int math) {this.chinese = chinese;this.math = math;
}public void setChinese(int chinese) {this.chinese = chinese;}public void setMath(int math) {this.math = math;}@Overridepublic String toString() {return "Mark{" +"chinese=" + chinese +", math=" + math +'}';}
}
public class User implements Serializable{private String name;private int age;private Mark mark;public User(String name, int age,Mark mark) {this.name = name;this.age = age;this.mark = mark;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", mark=" + mark +'}';}public static void main(String[] args) throws IOException, ClassNotFoundException {Mark mark = new Mark(100,99);User user = new User("user",22,mark);ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oo = new ObjectOutputStream(bo);oo.writeObject(user);//序列化ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi = new ObjectInputStream(bi);User userClone = (User) oi.readObject();//反序列化System.out.println("原user:"+user);System.out.println("克隆的user:"+userClone);user.mark.setMath(59);System.out.println("修改后的原user:"+user);System.out.println("修改后的克隆user:"+userClone);}
}

输出结果:

原user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=99}}
    克隆的user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=99}}
    修改后的原user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=60}}
    修改后的克隆user:User{name=‘user’, age=22, mark=Mark{chinese=100, math=99}}

带数组属性的克隆:

import java.io.*;
import java.util.Arrays;public class User implements Serializable{private String name;private int age;private int[] arr;public User(String name, int age, int[] arr) {this.name = name;this.age = age;this.arr = arr;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", arr=" + Arrays.toString(arr) +'}';}public static void main(String[] args) throws IOException, ClassNotFoundException {int[] arr = {1,2,3,4,5,6};User user = new User("user",22,arr);ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oo = new ObjectOutputStream(bo);oo.writeObject(user);//序列化ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi = new ObjectInputStream(bi);User userClone = (User) oi.readObject();//反序列化System.out.println("原user:"+user);System.out.println("克隆的user:"+userClone);user.arr[1] = 9;System.out.println("修改后的原user:"+user);System.out.println("修改后的克隆user:"+userClone);}
}

java对象的浅克隆和深克隆相关推荐

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

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

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

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

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

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

  4. JS:对象的浅克隆、深克隆

    JS中对象的深浅克隆 一.对象的浅克隆 1.浅克隆:只克隆对象的"表层",如果对象的某些属性值又是引用类型值,则不进一步克隆它们,只是传递它们的引用 2.使用for-in-循环即可 ...

  5. 对象的浅克隆与深克隆

    2019独角兽企业重金招聘Python工程师标准>>> 首先我们要知道为什么要进行对象克隆,我认为最大好处就是提高了效率,你想想用一个对象需要new一次的效率是非常低的. 这是基本的 ...

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

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

  7. Java对象克隆方法(浅克隆、深克隆)

    要让一个对象进行克隆,其实就是两个步骤: 1. 让该类实现java.lang.Cloneable接口: 2. 重写(override)Object类的clone()方法. 上面的克隆方法其实是浅克隆, ...

  8. Java浅克隆和深克隆

    浅克隆 在浅克隆中,如果原型对象的成员变量是值类型(int.double.byte.boolean.char等基本数据类型),将数据复制一份给克隆对象:如果原型对象的成员变量是引用类型(类.接口.数组 ...

  9. java 浅克隆_(Java)浅克隆与深克隆的区别

    克隆,就是复制一个对象的副本,而克隆又分浅克隆和深克隆.浅克隆是指克隆得到的对象基本类型的值改变了,而源对象的值不会变.但如果被克隆对象引用类型的值改变了,那么源对象的值同样会改变,因为引用类型在栈内 ...

最新文章

  1. CES上百度无人车队炫技,陆奇要用“China speed”改变世界
  2. 解决在VS(winform)程序中无法在调试时修改代码的问题(也就是“编辑并继续”功能无效)...
  3. app手机端连接tomcat电脑端服务器
  4. Git学习笔记:Git简介
  5. 如何写好一份简历-校招篇
  6. linux设备驱动之串口移植,Linux设备驱动之UART驱动结构
  7. HttpClient上传下载文件
  8. springboot+vue全栈开发_springboot+vue(一)___开发环境以及前后端项目搭建
  9. 关于setInterval设置倒计时只执行一次,clearInterval停止
  10. sklearn实践之——计算回归模型的四大评价指标(explained_variance_score、mean_absolute_error、mean_squared_error、r2_score)
  11. linux 如何查看 块设备_理解Linux操作系统中的块设备
  12. 如何在linux系统下读写windows上的共享文件夹
  13. fluent python_流畅的Python-Fluent Python简要书评
  14. 系统试运行报告_水污染源在线监测系统验收内容及模板(新版)
  15. Java使用腾讯企业邮箱发邮件的例子
  16. Exception in thread “main“ java.lang.ClassCastException 类型转换异常
  17. 新版本glib使用epoll代替poll
  18. 解决:word文档中插入照片有一部分不显示
  19. 再编写代码中报错:CS8107 C# 7.0 中不支持功能“xxxxxx”。请使用 7.1 或更高的语言版本。...
  20. 回归预测 | MATLAB实现GWO-LSTM灰狼算法优化长短期记忆神经网络多输入单输出回归预测

热门文章

  1. [3G/4G]3G/4G模块开发
  2. mkdir 创建目录
  3. Unity Inspector 给组件自动关联引用
  4. 如何去除Android应用中的广告
  5. 使用Lucene检索文档中的关键字
  6. 服务器计时器、Windows 计时器和线程计时器
  7. 【Python笔记】字典
  8. 关于AAARR模型,还停留在理论却不会用?附实例讲解
  9. 用数据说话,基金经理一年到底能挣多少钱?
  10. 化工企业数据分析平台项目之应收款分析