在学习深拷贝和浅拷贝之前,咱们先来一个例子:

import java.util.ArrayList;public class MyBaby implements Cloneable {/*** 私有变量*/private ArrayList<String> list = new ArrayList<>();@Overrideprotected Object clone() throws CloneNotSupportedException {MyBaby myBaby = null;try {myBaby = (MyBaby) super.clone();} catch (CloneNotSupportedException ex) {ex.printStackTrace();}return myBaby;}/*** 给List设置** @param value 值*/public void setValue(String value) {this.list.add(value);}/*** 获取list** @return list*/public ArrayList<String> getValue() {return this.list;}
}

在MyBaby类中有一个私有变量list,类似为List,然后咱们使用setValue对其进行设值,使用getValue进行取值。接下来咱们来看看他是如何拷贝的。

public class TestMyBaby {public static void main(String[] args) {MyBaby baby = new MyBaby();baby.setValue("Java后端技术栈");try {MyBaby myBabyClone = (MyBaby) baby.clone();myBabyClone.setValue("咖啡");System.out.println(baby.getValue());} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

猜想运行结果会是神马?

[Java后端技术栈, 咖啡]

怎么会这样呢?怎么会有“咖啡”呢?

是因为Java给我们做了一个偷懒性的拷贝动作,Object类原本就提供一个方法clone用来拷贝对象,因为其对象内部的数组、引用对象等都不拷贝,还是指向了原生对象的内部元素地址,这种拷贝就叫做浅拷贝

浅拷贝

上面这个拷贝也太浅了吧,两个对象引用都boby、myBaby共享一个私有变量list,都可以对list进行改变,是一种非常不安全的方式。

再看一个例子;

public class Person implements Cloneable {private int age;private String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {Person person=null;try{person=(Person)super.clone();}catch (CloneNotSupportedException ex){ex.printStackTrace();}return person;}
}

来写一个测试类,对其clone方法进行测试:

public class TestPerson {public static void main(String[] args) throws CloneNotSupportedException {Person person = new Person();person.setAge(22);person.setName("Java后端技术栈");//克隆一个对象Person clone = (Person) person.clone();//对person的age重新赋值为25person.setAge(25);//对person的age重新赋值为25person.setName("咖啡");System.out.print(clone.getAge()+","+clone.getName());}
}

运行后将输出什么呢?先猜想一下。

具体运行结果如下:

22,Java后端技术栈

是不是觉得很神奇呢?为什么没有变化呢?

原始数据类型会被拷贝,如果从原始数据类型考虑,因为age是int类型,int是原始数据类型,所以上述场景没变,那也就无话可说,但是String并不是原始数据类型,那又是为什么呢?因为String是一个特殊类型,因为这种场景下Java希望String也看成原始类型。所以String并没有clone方法。

String aa="aa";
aa.clone();

String定义

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {//...省略其他
}

这段代码编译通不过,提示无法访问,为什么呢?请看Object源码中对clone方法的定义

  protected native Object clone() throws CloneNotSupportedException;

String处理机制比较特殊,通过字符串池在需要的时候再内存中创建新的字符串,以后大家就在使用(clone)的时候就直接把String当做原始数据类型就行了。

深拷贝

浅拷贝是有风险的,那么如何才能深拷贝呢?我们对前面的Mybaby程序进行修改一下就成了深拷贝了;

import java.util.ArrayList;/*** @author tianweichang* @date 2019/7/13*/
public class MyBaby implements Cloneable {/*** 私有变量*/private ArrayList<String> list = new ArrayList<>();@SuppressWarnings("unchecked")@Overrideprotected Object clone() throws CloneNotSupportedException {MyBaby myBaby = null;try {myBaby = (MyBaby) super.clone();//增加了一个list.clone();this.list = (ArrayList<String>) this.list.clone();} catch (CloneNotSupportedException ex) {ex.printStackTrace();}return myBaby;}/*** 给List设置** @param value 值*/public void setValue(String value) {this.list.add(value);}/*** 获取list** @return list*/public ArrayList<String> getValue() {return this.list;}
}

再次运行TestMyBaby,结果:

[Java后端技术栈]

改短代码就实现了完全的拷贝,两个对象引用指向的就不再是同一个地址了。相互之间没有什么关系了,你修改你的,我修改我的,完全不会有什么安全问题。这就是深拷贝

深拷贝还有一种实现方式:通过写自己的二进制流来操作对象,然后实现对象的深拷贝。

建议

深拷贝和浅拷贝不要混合使用,特别是在涉及到类的继承时候,父类中有多个引用的情况下就会非常复杂,建议方案是深拷贝和浅拷贝分开实现。

clone与final两个冤家

对象的clone与对象内的final关键字是有冲突,前者是要重新赋值,后者是赋值了就不能变了。

咱们继续帮上忙的代码进行改造:

public class MyBaby implements Cloneable {/*** 私有变量*/private final ArrayList<String> list = new ArrayList<>();@SuppressWarnings("unchecked")@Overrideprotected Object clone() throws CloneNotSupportedException {MyBaby myBaby = null;try {myBaby = (MyBaby) super.clone();//下面的this.list编译通不过,提示不能给final修饰的变量重新赋值this.list = (ArrayList<String>) this.list.clone();} catch (CloneNotSupportedException ex) {ex.printStackTrace();}return myBaby;}/*** 给List设置** @param value 值*/public void setValue(String value) {this.list.add(value);}/*** 获取list** @return list*/public ArrayList<String> getValue() {return this.list;}
}

所以请注意,要使用clone方法的时候,类的成员变量上不要加final修饰。

java list拷贝_深入了解浅拷贝与深拷贝相关推荐

  1. Java拷贝(赋值、浅拷贝、深拷贝)

    文章目录 拷贝 直接赋值 浅拷贝 实现方式 特殊情况 深拷贝 实现方式 多层克隆 拷贝 直接赋值 直接赋值的方式没有生产新的对象,只是生新增了一个对象引用 浅拷贝 如果原型对象的成员变量是值类型,将复 ...

  2. 拷贝Python对象、浅拷贝、深拷贝

    浅拷贝和深拷贝在C++中出现和使用的较多,python也有相应的用法. test1 = ['a','c','b','d','f'] print id(test1) test2 = test1 prin ...

  3. python函数方法里面用浅复制深复制_图解 Python 浅拷贝与深拷贝

    Python 中的赋值语句不会创建对象的拷贝,仅仅只是将名称绑定至一个对象.对于不可变对象,通常没什么差别,但是处理可变对象或可变对象的集合时,你可能需要创建这些对象的 "真实拷贝" ...

  4. java 防止拷贝_[改善Java代码]避免对象的浅拷贝

    建议43: 避免对象的浅拷贝 我们知道一个类实现了Cloneable接口就表示它具备了被拷贝的能力,如果再覆写clone()方法就会完全具备拷贝能力.拷贝是在内存中进行的,所以在性能方面比直接通过ne ...

  5. nio java 内核拷贝_大文件拷贝,试试NIO的内存映射

    最近项目里有个需求需要实现文件拷贝,在java中文件拷贝流的读写,很容易就想到IO中的InputStream和OutputStream之类的,但是上网查了一下文件拷贝也是有很多种方法的,除了IO,还有 ...

  6. mysql浅拷贝_深入理解浅拷贝和深拷贝

    0x01:概述 Java中的对象拷贝 ( Object Copy ) 是指将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去.例如,对象 A 和对象 B 都属于类 S,具有属性 a ...

  7. java基础知识点_「Java面试题/知识点精华集」20000+字的Java基础知识篇(2020最新版) !

    " 本文已经收录进我的 79K Star 的 Java 开源项目 JavaGuide:https://github.com/Snailclimb/JavaGuide (「Java学习+面试指 ...

  8. java clone原理_详解Java中的clone方法 -- 原型模式

    Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...

  9. 引用拷贝、浅拷贝和深拷贝区别

    类型 拷贝一般分为二大类 引用拷贝 和 对象拷贝,我们通常讲的深拷贝和浅拷贝都属于对象拷贝. 引用拷贝 顾名思义,即是对引用地址的拷贝,说明引用地址一样,指向堆中的对象是同一个对象. 如果对一个对象进 ...

最新文章

  1. Boost filesystem学习笔记
  2. Akka源码分析-Remote-发消息
  3. oracle cube排序,Oracle rollup cube 用法
  4. 【面试招聘】聊聊求职过程中的技术面试
  5. 树莓派装系统,配置,换源,远程操控
  6. 中国大学MOOC-数据结构基础习题集、06-3、公路村村通
  7. NeurIPS 2019论文盘点:谷歌系最多,国内清华第一
  8. mysql读写分离的含义_mysql的读写分离问题剖析
  9. 20个使用柔和的色调的优秀网站设计示例
  10. 麟龙指标通达信指标公式源码_麟龙饱和度公式源码副图指标公式-通达信公式 -程序化交易(CXH99.COM)...
  11. oracle 数据库 双机,oracle双机热备份方法
  12. MarkDown 分割线
  13. 从程序员到项目经理(九):程序员加油站 — 再牛也要合群
  14. 使用Excel 提取文本中的数字
  15. 如何删除word中表格后面的空白页
  16. 再见 Win10!再见操作系统!
  17. -1-0 Java 简介 java是什么 java简单介绍
  18. VDA6.5认证咨询,产品审核与其他审核方式及检验的区别
  19. Excel之利用Excel的排序功能可以让表格整体按照某一列降序排列而排列(一)
  20. 【R语言中如何去除替换NA相关操作】

热门文章

  1. django使用mysql事务处理_Django中MySQL事务的使用
  2. android点击地址调用地图,Android 实现点击按钮 调用手机外部地图导航
  3. mysql boolean 和bool_关于 MySQL 的 boolean 和 tinyint(1)
  4. vue 日期格式化返回指定个数月份_12、vue中日期格式化转换的函数
  5. Java下载文件的几种方式
  6. springboot配置多项目下统一切换不同环境变量profile办法
  7. redisTemplate.opsForValue()中方法讲解
  8. try catch finally 中包含return的几种情况,及返回结果
  9. c mysql web开发实例教程_Web开发(六)MySql
  10. MySQL可运行在不同的操作系统下_不同操作系统下的mysql数据库同步