Java深层复制方式
为什么需要深层复制
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深层复制方式的更多相关文章
- java数组对象的浅层复制与深层复制
实际上,java中数组对象的浅层复制只是复制了对象的引用(参考),而深层复制的才是对象所代表的值.
- java多种文件复制方式以及效率比较
1.背景 java复制文件的方式其实有很多种,可以分为 传统的字节流读写复制FileInputStream,FileOutputStream,BufferedInputStream,BufferedO ...
- Java 对象复制
Java 对象的一共有 3 种复制对象的方式. 1.直接赋值 (引用复制 ),此种复制方式比较常用. 诸如 A a = b ; a 是直接复制了b的引用 ,也就是说它俩指向的是同一个对象. 此时 ...
- js中的深层复制
同java一样,数据的复制,不小心就是一个浅复制,莫名其妙的数据就被修改了,所以我们需要考虑深层复制的问题.这里提供一个深层复制的方法. 1.脚本 /** * 深层复制 */ cloneObject ...
- java 四种方式实现字符流文件的拷贝对比
将D:\\应用软件\\vm.exe 拷贝到C:\\vm.exe 四种方法耗费时间对比 4>2>3>1 package Copy; import java.io.Buffere ...
- java 传参方式--值传递还是引用传递
java 传参方式--值传递还是引用传递 参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递.写它是为了揭穿普遍存在的一种神话,即认为 Java 应用程序按引用 ...
- Java 使用线程方式Thread和Runnable,以及Thread与Runnable的区别
一. java中实现线程的方式有Thread和Runnable Thread: public class Thread1 extends Thread{ @Override public void r ...
- java通过jni方式获取硬盘序列号(windows,linux)
linux系统java通过jni方式获取硬盘序列号 http://blog.csdn.net/starter110/article/details/8186788 使用jni在windows下读取硬盘 ...
- Java 初始化字段方式和顺序
Java 初始化字段方式和顺序: 类加载时直接初始化静态字段; 类加载时调用静态方法初始化静态字段; 实例化对象时,在调用构造函数之前代码块中初始化字段; 实例化对象时,在调用构造函数之时初始化字段; ...
随机推荐
- spring中获取容器中的Bean为什么前转成接口而不是实现类
简单介绍一下上下文,userService是服务层接口有一个save方法,userServiceImpl是该接口的实现类重写了save方法. applicationContext.xml如图: 后台代 ...
- 关于HTML的引入CSS文件问题
一 html代码引用外部css文件时若css文件在本文件的父目录下的其他目录下,可使用绝对路径.此时路径要写为 “ ../ ”形式,如在tomcat下建立一个test文件,在该文件中建立两个文件 夹 ...
- Spring WebClient vs. RestTemplate
1. 简介 本教程中,我们将对比 Spring 的两种 Web 客户端实现 -- RestTemplate 和 Spring 5 中全新的 Reactive 替代方案 WebClient. 2. 阻塞 ...
- Thinkphp 5.1.7 parseData缺陷导致insert/update注入 分析
目录 环境搭建 分析 参考 环境搭建 $ composer create-project topthink/think thinkphp-5.1.7 修改composer.json 5.1.* =&g ...
- VSTO之PowerPoint(PPT)插件开发常用API汇总
VSTO简介 VSTO(Visual Studio Tools for Office )是VBA的替代,使得开发Office应用程序更加简单,并且用VSTO来开发office应用程序可以使用Visua ...
- Extjs的textfield的颜色设置和出现的问题笔记
Ext.getCmp('alarmsLevelVal').setFieldStyle('background-color:#ff0000;background-p_w_picpath: none; ' ...
- 实时同步lsyncd
实时同步lsyncd 1 lsyncd 1.1 lsyncd 简介 Lsyncd使用文件系统事件接口(inotify或fsevents)来监视对本地文件和目录的更改.Lsyncd将这些事件整理几秒钟, ...
- 【有容云案例系列】基于Jenkins和Kubernetes的CI工作流
嘉宾介绍 黄文俊 有容云资深系统架构师 主要负责容器云平台产品架构及设计. 8年工作经验, 有着企业级存储, 云计算解决方案相关理解. 关注于微服务设计思考, 开发流程优化, docker及kuber ...
- 利用Idea重构功能及Java8语法特性——优化深层嵌套代码
当遇到深层嵌套代码,如for,if,lambda表达式或内部类及这些代码的组合,这时我们可以通过Java 8的语法特性来进行优化. 下面的代码是一个嵌套循环的示例. public MappedFiel ...
- vue过滤器的使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...