java deep clone util_Java Clone深拷贝与浅拷贝的两种实现方法
1.首先,你要知道怎么实现克隆:实现Cloneable接口,在bean里面重写clone()方法,权限为public。
2.其次,你要大概知道什么是地址传递,什么是值传递。
3.最后,你要知道你为什么使用这个clone方法。
先看第一条,简单的克隆代码的实现。这个也就是我们在没了解清楚这个Java的clone的时候,会出现的问题。
看完代码,我再说明这个时候的问题。
先看我要克隆的学生bean的代码:
package com.lxk.model;
/**
* 学生类:有2个属性:1,基本属性-String-name;2,引用类型-Car-car。
*
* Created by lxk on 2017/3/23
*/
public class Student implements Cloneable {
private String name;
private Car car;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", car=" + car +
'}';
}
@Override
public Student clone() {
Student student = null;
try {
student = (Student) super.clone();
} catch (CloneNotSupportedException ignored) {
System.out.println(ignored.getMessage());
}
return student;
}
}
学生内部引用了Car这个bean
package com.lxk.model;
import java.util.List;
public class Car implements Comparable {
private String sign;
private int price;
private List myDog;
private List boys;
public Car() {
}
public Car(String sign, int price) {
this.sign = sign;
this.price = price;
}
public Car(String sign, int price, List myDog) {
this.sign = sign;
this.price = price;
this.myDog = myDog;
}
public Car(String sign, int price, List myDog, List boys) {
this.sign = sign;
this.price = price;
this.myDog = myDog;
this.boys = boys;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public List getMyDog() {
return myDog;
}
public void setMyDog(List myDog) {
this.myDog = myDog;
}
public List getBoys() {
return boys;
}
public void setBoys(List boys) {
this.boys = boys;
}
@Override
public int compareTo(Car o) {
//同理也可以根据sign属性排序,就不举例啦。
return this.getPrice() - o.getPrice();
}
@Override
public String toString() {
return "Car{" +
"sign='" + sign + '\'' +
", price=" + price +
", myDog=" + myDog +
", boys=" + boys +
'}';
}
}
最后就是main测试类
package com.lxk.findBugs;
import com.lxk.model.Car;
import com.lxk.model.Student;
/**
* 引用传递也就是地址传递需要注意的地方,引起的bug
*
* Created by lxk on 2017/3/23
*/
public class Bug2 {
public static void main(String[] args) {
Student student1 = new Student();
Car car = new Car("oooo", 100);
student1.setCar(car);
student1.setName("lxk");
//克隆完之后,student1和student2应该没关系的,修改student1不影响student2的值,但是完之后发现,你修改car的值,student2也受影响啦。
Student student2 = student1.clone();
System.out.println("学生2:" + student2);//先输出student2刚刚克隆完之后的值,然后在修改student1的相关引用类型的属性值(car)和基本属性值(name)
car.setSign("X5");
student1.setName("xxx");
System.out.println("学生2:" + student2);//再次输出看修改的结果
}
}
之后就该是执行的结果图了:
对上面执行结果的疑惑,以及解释说明:
我们可能觉得自己在bean里面实现clone接口,重写了这个clone方法,那么学生2是经由学生1clone,复制出来的,
那么学生1和学生2,应该是毫不相干的,各自是各自,然后,在修改学生1的时候,学生2是不会受影响的。
但是结果,不尽人意。从上图执行结果可以看出来,除了名字,这个属性是没有被学生1影响,关于car的sign属性已经因为学生1的变化而变化,这不是我希望的结果。
可见,这个简单的克隆实现也仅仅是个“浅克隆”,也就是基本类型数据,他是会给你重新复制一份新的,但是引用类型的,他就不会重新复制份新的。引用类型包括,上面的其他bean的引用,list集合,等一些引用类型。
那么怎么实现深克隆呢?
对上述代码稍作修改,如下:
学生bean的clone重写方法如下所示:
@Override
public Student clone() {
Student student = null;
try {
student = (Student) super.clone();
if (car != null) {
student.setCar(car.clone());
}
} catch (CloneNotSupportedException ignored) {
System.out.println(ignored.getMessage());
}
return student;
}
然后还要Car类实现cloneable接口,复写clone方法:
@Override
public Car clone() {
Car car = null;
try {
car = (Car) super.clone();
if (myDog != null) {
car.setMyDog(Lists.newArrayList(myDog));
}
if (boys != null) {
car.setBoys(Lists.newArrayList(boys));
}
} catch (CloneNotSupportedException ignored) {
System.out.println(ignored.getMessage());
}
return car;
}
主测试代码不动,这个时候的执行结果如下:
可以看到,这个时候,你再修改学生1的值,就不会影响到学生2的值,这才是真正的克隆,也就是所谓的深克隆。
怎么举一反三?
可以看到,这个例子里面的引用类型就一个Car类型的属性,但是实际开发中,除了这个引用其他bean类型的属性外,可能还要list类型的属性值用的最多。
那么要怎么深克隆呢,就像我在Car bean类里面做的那样,把所有的引用类型的属性,都在clone一遍。那么你在最上层调用这个clone方法的时候,他就是真的深克隆啦。
我代码里面那么判断是为了避免空指针异常。当然,这个你也得注意咯。
注意 重写clone方法的时候,里面各个属性的null的判断哦。
上面的是override clone()方法来实现深克隆的。如果你这个要克隆的对象很复杂的话,你就不得不去每个引用到的对象去复写这个clone方法,这个太啰嗦来,改的地方,太多啦。
还有个方法就是使用序列化来实现这个深拷贝
/**
* 对象的深度克隆,此处的对象涉及Collection接口和Map接口下对象的深度克隆
* 利用序列化和反序列化的方式进行深度克隆对象
*
* @param object 待克隆的对象
* @param 待克隆对象的数据类型
* @return 已经深度克隆过的对象
*/
public static T deepCloneObject(T object) {
T deepClone = null;
ByteArrayOutputStream baos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
bais = new ByteArrayInputStream(baos
.toByteArray());
ois = new ObjectInputStream(bais);
deepClone = (T)ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if(baos != null) {
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try{
if(bais != null) {
bais.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try{
if(ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return deepClone;
}
具体的使用如下:
/**
* 使用序列化来实现深拷贝简单。但是,所涉及到的所有对象都的实现序列化接口。
*/
private static void cloneBySerializable() {
Student student1 = new Student();
Car car = new Car("oooo", 100, Lists.newArrayList(new Dog("aaa", true, true)));
student1.setCar(car);
student1.setName("lxk");
Student student2 = deepCloneObject(student1);
System.out.println("学生2:" + student2);
car.setSign("X5");
car.setMyDog(null);
student1.setName("xxx");
System.out.println("学生2:" + student2);
}
实现的效果,还是和上面的一样的,但是这个就简单多来,只需要给涉及到的每个引用类型,都去实现序列化接口就好啦。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对聚米学院的支持。如果你想了解更多相关内容请查看下面相关链接
java deep clone util_Java Clone深拷贝与浅拷贝的两种实现方法相关推荐
- win8 java不是内部或外部命令_win8系统运行java提示“ava不是内部或外部命令两种解决方法...
Java一种可以撰写跨平台应用软件的面向对象的程序设计语言,最近有一些小伙伴使用win10操作系统的时候遇到了一个问题,win8系统win8系统运行java突然提示"java不是内部或外部命 ...
- java拷贝函数_Java的深拷贝与浅拷贝的几种实现方式
1.介绍 关于Java的深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象.可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传 ...
- java 计时 timeclock_计时器的time_t和clock_t 的两种实现方法(推荐)
想给自己初步完成的相空间搜索算法计算一下运行时间,于是尝试了如下使用 time_t 类型的方式 #include #include #include #include "StateFunct ...
- java 复制Map对象(深拷贝与浅拷贝)
java 复制Map对象(深拷贝与浅拷贝) CreationTime--2018年6月4日10点00分 Author:Marydon 1.深拷贝与浅拷贝 浅拷贝:只复制对象的引用,两个引用仍然指向同一 ...
- java代码二进制转为十六进制_Java 中二进制转换成十六进制的两种实现方法
Java 中二进制转换成十六进制的两种实现方法 每个字节转成16进制,方法1 /** * 每个字节转成16进制,方法1 * * @param result */ private static Stri ...
- Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理
Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理 代理模式 JDK动态代理 CGLIB动态代理 代理模式 代理模式是23种设计模式的一种,指一个对象A通过持有另一个对象B,可以具有B ...
- Java中关于Arrays.sort的两种重载方法的理解
前言 在java中重载排序方法的方法目前有两种,一种是实现Comparable接口的compareTo方法,还有一种是用比较器(comparator) 作为参数,其中比较器是实现了Comparator ...
- 用java实现云计算的两种趋势性方法
本文讲的是用java实现云计算的两种趋势性方法,[IT168 资讯]最近,人们在思考如何使用Java来实现云计算,我看到了两种趋势性的方法.它们存在交叉重叠处而并非互相排斥,但总体而言它们在项目方面有 ...
- 按照姓名升序排序的代码_好程序员Java培训分享Java集合的两种排序方法
好程序员Java培训分享Java集合的两种排序方法,Java集合的工具类Collections中提供了两种排序的方法,分别是: 1.Collections.sort(List list) 2.Coll ...
最新文章
- 只有ajax会跨域吗_ajax处理跨域有几种方式
- Transformer又来搞事情!百万像素高清图轻松合成,效果迷人
- 内网穿透从搭建到溯源
- Erlang与Java内存架构
- 较真的来了!这篇【硬核论文】为何恺明新作MAE提供了一种理论解释和数学证明...
- Python 基础 - Day 2 Learning Note - 字符转编码操作
- Factory Method (工厂模式)
- GitHub 上数十个 NetBeans 开源项目被卷入供应链攻击
- 2万亿市值公司的网络运营技术解密
- 关于springMVC的日志管理
- java哈夫曼编码译码_java实现哈夫曼编码
- linux内存的优化大师,Linux性能优化大师(调整操作系统参数)
- JZOJ1205. 帮助Bubu(2017.8B组)
- 以太坊:分片Sharding FAQ
- windows 10 ltsc 安装微软商店
- 地图编辑器开发(二)
- matlab:使用4阶龙格库塔方法求解常微分方程组
- win10虚拟机安装linux
- 算法分析与设计实验报告 ——二分搜索程序算法的实现
- 局域网通信软件MTalk
热门文章
- IPRO_DOCXCC_FILLIN_UUIDTONAME
- SAP Leonardo机器学习如何获取模型存储的实际地址
- IPM: 使用代码删除Acquisition contract上的IP Product
- CRM 里面table download to excel的实现
- 如何在Netweaver SE16里直接查看某数据库行记录 1
- Jerry的ABAP原创技术文章合集
- 2021-04-10 【数据库导数】数字类型的列如果位数过长,变为科学计数法问题
- java扫雷具有win7_Win7系统自带扫雷游戏打不开的解决方法
- 几何画板200个经典课件_动态几何画板 Geogebra
- 多用户企业文件管理系统源码_固定资产管理系统的细节分析