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 初始化字段方式和顺序: 类加载时直接初始化静态字段; 类加载时调用静态方法初始化静态字段; 实例化对象时,在调用构造函数之前代码块中初始化字段; 实例化对象时,在调用构造函数之时初始化字段; ...
随机推荐
- C语言数据类型及变量整理
数据类型 获取int的字节数大小方法 printf("int bytes:%d",sizeof(int)); 列表整理 类型 字节数 取值范围 char 1 [-128,127]= ...
- 提升10倍生产力:IDEA远程一键部署SpringBoot到Docker
作者:陶章好 juejin.im/post/5d026212f265da1b8608828b 推荐阅读(点击即可跳转阅读) 1. SpringBoot内容聚合 2. 面试题内容聚合 3. 设计模式内容 ...
- 04-kubernetes网络通信
目录 kubernetes网络通信 需要解决的问题 flannel Calico/Cannel kubernetes网络通信 需要解决的问题 同一个pod内部的不同容器间通信, local Pod间的 ...
- 【iOS】PLA 3.3.12
发件人 Apple Program License Agreement PLA We found that your app uses the Advertising Identifier but d ...
- CodeForces 372 A. Counting Kangaroos is Fun
题意,有n只袋鼠,没每只袋鼠有个袋子,大小为si,一个袋鼠可以进入另外一个袋鼠的袋子里面,当且仅当另一个袋鼠的袋子是他的二倍或二倍一上,然后中国袋鼠就是不可见的,不能出现多个袋鼠嵌套的情况.让你求最少 ...
- win10家庭版打开组策略
新建记事本,输入: @echo off pushd "%~dp0" dir /b C:\Windows\servicing\Packages\Microsoft-Windows-G ...
- 四、Python基础(1)
目录 四.Python基础(1) 四.Python基础(1) 1.什么是变量? 一种变化的量,量是记录世界上的状态,变指得是这些状态是会变化的. 2.为什么有变量? 因为计算机程序的运行就是一系列状态 ...
- 深入理解JVM-类加载器深入解析(1)
类加载 在java代码中,类型的加载,连接与初始化过程都是在程序运行期间完成的 类型:表示的Object本身,并不是指一个对象,也就是class. 运行期间:表示的是一种runtime的概念,在运行期 ...
- 使用webstorm搭建vue-cli项目
前言 随着vue在前端不断的壮大,越来越多的前端工程师使用vue了,作为大型项目的开发,vue-cli是不二之选,所以这篇博客是为搭建vue-cli所写,想要搭建vue-cli项目就必须先有git,n ...
- Web开发中的相对路径和绝对路径
在学习HTML的时候一定会遇到引入文件和链接跳转页面,比如:JS文件.CSS文件.Image图片.我们就会考虑是相对路径和绝对路径的问题.下面PHP程序员雷雪松就详细讲解下Web开发中的相对路径和绝对 ...