为什么需要深层复制

Object 的 clone() 方法是浅层复制(但是 native 很高效)。
另外,Java 提供了数组和集合的复制方法,分别是 Arrays.copy() 和 Collections.copy() 方法。
前者实际上使用了 System.arraycopy() 方法,两者其实也是浅层复制,过程类似于下面的 for 循环:

for(int i=0; i<len; i++){
  dest[i] = src[i];
}

所以当数组或集合中元素是对象时,只是做了引用的复制,指向的还是堆中同一个对象。

一般有两种深层复制方案

1)实现 Cloneable 接口

包装 Object.clone() ,根据属性类型深度 clone。

这种方法,使用了 Object.clone() ,优点是 native 方法性能好,缺点是实现太繁琐。

/**
* 0 实现 Cloneable 接口
* 1 包装 super.clone(),提供 public 方法
* 2 默认的是浅层复制
* @author Super
*
*/
public class shallowCloneTest { public static void main(String[] args) {
Resource0 r0 = new Resource0(0, "资源1号");
Resource0 r1 = new Resource0(1, "内部资源");
r0.setInnerResource(r1); //验证克隆
Resource0 r2 = r0.shallowClone();
System.out.println(r0);
System.out.println(r2);
System.out.println(r1==r2); //false //验证浅度克隆
r2.getInnerResource().setId(7);
System.out.println(r1); //受影响了
} } /**
* 深层复制方案一:包装 clone,引用变量继续 clone
* @author Super
*
*/
public class DeepCloneTest1 { public static void main(String[] args) {
Resource0 r0 = new Resource0(0, "资源1号");
Resource0 r1 = new Resource0(1, "内部资源");
r0.setInnerResource(r1); //验证克隆
Resource0 r2 = r0.deepClone();
System.out.println(r0);
System.out.println(r2);
System.out.println(r1==r2); //false //验证深度度克隆
r2.getInnerResource().setId(7);
System.out.println(r1); //不受影响
} } public class Resource0 extends BaseVo implements Cloneable { private static final long serialVersionUID = 1L; private Integer id;
private String name;
private Resource0 innerResource; public Resource0(Integer id, String name) {
super();
this.id = id;
this.name = name;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Resource0 getInnerResource() {
return innerResource;
} public void setInnerResource(Resource0 innerResource) {
this.innerResource = innerResource;
} /**
* 浅层复制
* @return
*/
public Resource0 shallowClone(){
Resource0 r = null;
try {
r = (Resource0)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return r;
} /**
* 深层复制
* @return
*/
public Resource0 deepClone(){
Resource0 r = null;
try {
r = (Resource0)super.clone();
r.innerResource = (Resource0) innerResource.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return r;
} @Override
public String toString() {
return "Resource0 [id=" + id + ", name=" + name + ", innerResource="
+ innerResource + "]";
}
}

2)对象序列化输入输出

这种方法,强大且简单,先写到内存,再读出来。

PS:单例对象最好也关注下是否有被序列化复制的风险。

/**
* 深层复制方案2:输入输出序列化
* @author Super
*
*/
public class DeepCloneTest2 { public static void main(String[] args) {
Resource0 r0 = new Resource0(0, "资源1号");
Resource0 r1 = new Resource0(1, "内部资源");
r0.setInnerResource(r1); //验证克隆
Resource0 r2 = (Resource0) IOUtil.deepClone(r0);
System.out.println(r0);
System.out.println(r2);
System.out.println(r1==r2); //false //验证深度度克隆
r2.getInnerResource().setId(7);
System.out.println(r1); //不受影响
} } /**
* 深层复制序列化 vo
* @param src
* @return dest
* @throws IOException
* @throws ClassNotFoundException
*/
public static BaseVo deepClone(BaseVo src) {
ByteArrayOutputStream bo = null;
ObjectOutputStream out = null;
ObjectInputStream in = null;
BaseVo dest = null;
try{
try{
//对象写入内存
bo = new ByteArrayOutputStream();
out = new ObjectOutputStream(bo);
out.writeObject(src);
//从内存中读回来
in = new ObjectInputStream(new ByteArrayInputStream(bo.toByteArray()));
dest = (BaseVo) in.readObject();
}finally{
//使用 finally 关闭资源
if(in!=null){
in.close();
}
if(out!=null){
out.close();
}
if(bo!=null){
bo.close();
}
}
//使用 catch 块统一捕捉资源
} catch(IOException | ClassNotFoundException ex){
ex.printStackTrace();
} return dest;
}

参考阅读:

https://www.jianshu.com/p/7aaaf884cc44

Java深层复制方式的更多相关文章

  1. java数组对象的浅层复制与深层复制

    实际上,java中数组对象的浅层复制只是复制了对象的引用(参考),而深层复制的才是对象所代表的值.

  2. java多种文件复制方式以及效率比较

    1.背景 java复制文件的方式其实有很多种,可以分为 传统的字节流读写复制FileInputStream,FileOutputStream,BufferedInputStream,BufferedO ...

  3. Java 对象复制

    Java 对象的一共有 3 种复制对象的方式. 1.直接赋值 (引用复制 ),此种复制方式比较常用. 诸如 A  a = b ;  a 是直接复制了b的引用 ,也就是说它俩指向的是同一个对象. 此时 ...

  4. js中的深层复制

    同java一样,数据的复制,不小心就是一个浅复制,莫名其妙的数据就被修改了,所以我们需要考虑深层复制的问题.这里提供一个深层复制的方法. 1.脚本 /** * 深层复制 */ cloneObject ...

  5. java 四种方式实现字符流文件的拷贝对比

    将D:\\应用软件\\vm.exe  拷贝到C:\\vm.exe   四种方法耗费时间对比  4>2>3>1 package Copy; import java.io.Buffere ...

  6. java 传参方式--值传递还是引用传递

    java 传参方式--值传递还是引用传递 参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递.写它是为了揭穿普遍存在的一种神话,即认为 Java 应用程序按引用 ...

  7. Java 使用线程方式Thread和Runnable,以及Thread与Runnable的区别

    一. java中实现线程的方式有Thread和Runnable Thread: public class Thread1 extends Thread{ @Override public void r ...

  8. java通过jni方式获取硬盘序列号(windows,linux)

    linux系统java通过jni方式获取硬盘序列号 http://blog.csdn.net/starter110/article/details/8186788 使用jni在windows下读取硬盘 ...

  9. Java 初始化字段方式和顺序

    Java 初始化字段方式和顺序: 类加载时直接初始化静态字段; 类加载时调用静态方法初始化静态字段; 实例化对象时,在调用构造函数之前代码块中初始化字段; 实例化对象时,在调用构造函数之时初始化字段; ...

随机推荐

  1. web设计_7_页面缺失图片或CSS的情况下仍然易读

    1. 在任何可能使用背景图片的地方应设置同样的颜色的背景色. 防止图片不能加载的情况下,页面内容同样保持较好可读性. 例如文字为白色,背景图为深色,如果不设置背景色,当背景图未成功加载, 而浏览器多数 ...

  2. +CIMG+彩色图片边缘提取实验记录_canny/hough transfrom

    前言: 书到用时方恨少 正文: 边缘提取技术一直都有接触,最通用的莫过于拉普拉斯,sobel几个算子,两个算子都可通过简单的模板运算进行,而现在比较好的一个边缘提取技术是canny,文章中我是用的ca ...

  3. javaweb入门----servlet简介

    servlet 上文已经了解了web服务器和http协议是怎么回事儿,并且也了解了浏览器与服务器之间的联系,现在要介绍一下服务器是如何处理来自客户端的请求的,这就是servlet. servlet:J ...

  4. TypeScript环境安装,以及配置idea开发环境

    安装前准备 安装typescript需要用到npm管理工具,而npm是随同Node.js一起安装的包管理工具,所以要先安装Node 1.前往Node官网下载最新版安装包:http://nodejs.c ...

  5. 并发编程之Java内存模型

    在介绍Java内存模型之前,先来了解一下为什么要有内存模型,以及内存模型是什么.然后我们基于对内存模型的了解,学习Java内存模型以及并发编程的三大特性. 为什么要有内存模型 在计算机中,所有的运算操 ...

  6. 【iOS】获取应用程序本地路径

    Xcode 会为每一个应用程序生成一个私有目录,并随机生成一个数字和字母串作为目录名,在每一次应用程序启动时,这个字母数字串都是不同于上一次. 所以通常使用 Documents 目录进行数据持久化的保 ...

  7. Codis与RedisCluster的原理详解

    背景介绍 我们先来看一下为什么要做集群,如果我们要部署一个单节点Redis,很明显会遇到单点故障的问题. 首先能想到解决单点故障的方法,就是做主从,但是当有海量存储需求时,单一的主从结构就会出问题,说 ...

  8. 数据结构-二叉搜索树和二叉树排序算法(python实现)

    今天我们要介绍的是一种特殊的二叉树--二叉搜索树,同时我们也会讲到一种排序算法--二叉树排序算法.这两者之间有什么联系呢,我们一起来看一下吧. 开始之前呢,我们先来介绍一下如何创建一颗二叉搜索树. 假 ...

  9. 运营商手机视频流量包业务日志ETL及统计分析

    自己做过的项目在这里做一个记录,否则就感觉不是自己的了.一是因为过去时间已经很长了,二是因为当时做得有点粗糙,最后还不了了之了. 话不多说,先大致介绍一下项目背景.以前各大手机视频 App 一般都有运 ...

  10. JDK1.8源码分析01之学习建议(可以延伸其他源码学习)

    序言:目前有个计划就是准备看一下源码,来提升自己的技术实力.同时现在好多面试官都喜欢问源码,问你是否读过JDK源码等等? 针对如何阅读源码,也请教了我的老师.下面就先来看看老师的回答,也许会有帮助呢. ...