JAVA中对象的克隆及深拷贝和浅拷贝
使用场景:
在日常的编程过程 中,经常会遇到,有一个对象OA,在某一时间点OA中已经包含了一些有效值 ,此时可能会需一个和OA完全相对的新对象OB,并且要在后面的操作中对OB的任何改动都不会影响到OA的值,也就是OA与Ob是需要完全两个独立的对象。
但OB的初始值是由对象OA确定的。在JAVA语言中,用普通的赋值语句是满足不了需求的。使用对象的clone()方法是实现克隆的最简单、也是最高效的手段。
Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。 实现克隆可以用深拷贝和浅拷贝来实现。
深拷贝和浅拷贝的基本概念的理解:
浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象
通过实例看深拷贝和浅拷贝的实现和不同:
浅拷贝
class AddressNew implements Cloneable {
private String add;
public String getAdd() {
return add;
}
public void setAdd(String add) {
this.add = add;
}
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class StudentNew implements Cloneable{
private int number;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private AddressNew addr;
public AddressNew getAddr() {
return addr;
}
public void setAddr(AddressNew addr) {
this.addr = addr;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class JavaShallowCopy {
public static void main(String [] args) throws Exception{
AddressNew addr = new AddressNew();
addr.setAdd("杭州市");
StudentNew stu1 = new StudentNew();
stu1.setNumber(123);
stu1.setName("s1");
stu1.setAddr(addr);
StudentNew stu2 = (StudentNew)stu1.clone();
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
System.out.println(stu1);
System.out.println(stu2);
addr.setAdd("西湖区");
//stu1.setNumber(20);
//stu2.setName("s2");
System.out.println("学生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
}
}
运行结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
com.songidea.StudentNew@133314b
com.songidea.StudentNew@b1bc7ed
学生1:s1,地址:西湖区
学生2:s1,地址:西湖区
从运行结果来看,stu1,stu2的解是2个不同的对象了,但是在改变了,addr的对象的地址之后,stu1,stu2的2个对象的引用对象addr的值都改变了,也就是说stu1和stu2的addr对象引用的是同一个地址,这个不是我们想要的结果,在实际的开发工作中这一块一定要特别注意,使用不当可能会使业务功能或数据造成错误或混乱,所以这个拷贝只是实现在浅拷贝,那么从我们需要的场景看需要实现深拷贝才能达到我们想要的结果,下面会通过实例来看深拷贝的2种不同的实现。
深拷贝:
class Address implements Cloneable {
private String add;
public String getAdd() {
return add;
}
public void setAdd(String add) {
this.add = add;
}
@Override
public Object clone() {
Address addr = null;
try{
addr = (Address)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return addr;
}
}
public class Student implements Cloneable{
private int number;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private Address addr;
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone(); //浅复制
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
stu.addr = (Address)addr.clone(); //深度复制
return stu;
}
}
public class javaDeepCopy {
public static void main(String args[]) {
Address addr = new Address();
addr.setAdd("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
stu1.setName("s1");
stu1.setAddr(addr); Student stu2 = (Student) stu1.clone(); System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
System.out.println(stu1);
System.out.println(stu2);
addr.setAdd("西湖区");
stu1.setNumber(20);
System.out.println("学生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
} }
运行结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
com.songidea.Student@133314b
com.songidea.Student@b1bc7ed
学生1:s1,地址:西湖区
学生2:s1,地址:杭州市
从运行结果来看,stu1,stu2的解是2个不同的对象了,在改变了addr的对象的地址之后,stu1,stu2的2个对象的引用对象addr的值只有stu1r 改变了,也就是说stu1和stu2的addr对象引用的不是同一个地址,这个是我们想要的结果所以这个拷贝只是实现在浅拷贝,那么从我们需要的场景看,这种方式实现了深拷,达到了我们想要的结果,下面会通过实例来看深拷贝的另一种实现:通过序例化来实现深拷贝。
深拷贝序列化的实现:
class AddressSerial implements Serializable {
private String add;
public String getAdd() {
return add;
}
public void setAdd(String add) {
this.add = add;
}
}
public class StudentSerial implements Serializable{
private int number;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private AddressSerial addr;
public AddressSerial getAddr() {
return addr;
}
public void setAddr(AddressSerial addr) {
this.addr = addr;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Object deepClone() throws IOException,OptionalDataException,ClassNotFoundException{
ByteArrayOutputStream bo=new ByteArrayOutputStream();
ObjectOutputStream oo=new ObjectOutputStream(bo);
oo.writeObject(this);
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi=new ObjectInputStream(bi);
return oi.readObject();
}
}
public class javaDeepCopySreial {
public static void main(String [] args) throws Exception{
AddressSerial addr = new AddressSerial();
addr.setAdd("杭州市");
StudentSerial stu1 = new StudentSerial();
stu1.setNumber(123);
stu1.setName("s1");
stu1.setAddr(addr);
StudentSerial stu2 = (StudentSerial)stu1.deepClone();
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
System.out.println(stu1);
System.out.println(stu2);
addr.setAdd("西湖区");
//stu1.setNumber(20);
//stu2.setName("s2");
System.out.println("学生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
}
}
运行结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
com.songidea.StudentSerial@1e80bfe8
com.songidea.StudentSerial@5e9f23b4
学生1:s1,地址:西湖区
学生2:s1,地址:杭州市
从运行的结果看序例化也达到了深拷贝的场景。
参考和阅读的几偏深拷贝浅拷贝的文章:
https://www.cnblogs.com/null00/archive/2010/12/14/2065088.html
https://www.cnblogs.com/xuanxufeng/p/6558330.html
https://blog.csdn.net/baiye_xing/article/details/71788741
JAVA中对象的克隆及深拷贝和浅拷贝的更多相关文章
- java对象的克隆以及深拷贝与浅拷贝
一.为什么要使用克隆 在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也 ...
- Java中的Cloneable接口与深拷贝、浅拷贝
Cloneable接口是一个标记接口,也就是没有任何内容,定义如下: 这里分析一下这个接口的用法,clone方法是在Object种定义的,而且是protected型的,只有实现了这个接口,才可以在该类 ...
- Java中对象的深复制和浅复制详解
1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵ ...
- java克隆之深拷贝与浅拷贝
版权声明:本文出自汪磊的博客,转载请务必注明出处. Java深拷贝与浅拷贝实际项目中用的不多,但是对于理解Java中值传递,引用传递十分重要,同时个人认为对于理解内存模型也有帮助,况且面试中也是经常问 ...
- 深入浅出Java中的clone克隆方法,写得太棒了!
作者:张纪刚 blog.csdn.net/zhangjg_blog/article/details/18369201/ Java中对象的创建 clone 顾名思义就是 复制 , 在Java语言中, c ...
- java 中对象比较大小
java 中对象比较大小 java 中对象比较大小有两种方法 1:实现Comparable 接口 的 public int compareTo(T o) 方法: 2:实现Comparator 接口 的 ...
- Java中对象、对象引用、堆、栈、值传递以及引用传递的详解
Java中对象.对象引用.堆.栈.值传递以及引用传递的详解 1.对象和对象引用的差别: (1).对象: 万物皆对象.对象是类的实例. 在Java中new是用来在堆上创建对象用的. 一个对象能够被多个引 ...
- Java中对象流使用的一个注意事项
再写jsp的实验作业的时候,需要用到java中对象流,但是碰到了之前没有遇到过的情况,改bug改到崩溃!!记录下来供大家分享 如果要用对象流去读取一个文件,一定要先判断这个文件的内容是否为空,如果为空 ...
- Java中对象和引用的理解
偶然想起Java中对象和引用的基本概念,为了加深下对此的理解和认识,特地整理一下相关的知识点,通过具体实例从两者的概念和区别两方面去更形象的认识理解,再去记忆. 一.对象和引用的概念: 在Java中万 ...
随机推荐
- Zabbix4.x 历史数据存储到Elasticsearch7.x
一.简介 Zabbix 3.4.6 版本开始支持历史数据存储到 Elasticsearch, 早就想测试这个功能,最近有个需求需保存 zabbix 的历史数据上达十亿条,因此决定测试这功能的实用性,事 ...
- Maven 打war包
使用maven将项目达成war包 右击项目的跟项目,选择run as 选择maven build.... 进入窗口,在 Goals 输入命令 clean package,选择 skip Tes ...
- sehll变量比较
1.比较符号解释 $# 表示提供到shell脚本或者函数的参数总数: $1 表示第一个参数. -ne 表示 不等于 $?是shell变量,表示"最后一次执行命令"的退出状态.0为成 ...
- 使用expect实现自动交互,shell命令行自动输入,脚本自动化,变量引用,expect spawn执行带引号命令,expect 变量为空,不生效,不能匹配通配符*,函数,数组
背景 有需求,在允许命令或者脚本跳出交互行,需要进行内容输入,但需要人手动输入,不是很方便,此时可以通过expect来实现自动互动交互. expect是一个自动交互功能的工具,可以满足代替我们实际工作 ...
- PPM / PGM / PBM 图像文件格式
PPM / PGM / PBM 图像文件格式 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 说明:在进行图像压缩后传输,然后解压缩显示的过程中,通常会用到P ...
- 1、5 写注册的后台并写前台html&密码加密&id 随机
1 public void save(Student student) { // TODO Auto-generated method stub student.setSid(UUID.randomU ...
- mui 打包发布ios 测试
1.首先在Hbuilder新建一个app项目,把你的代码放进来 2.在manifest.json里设置你想要的一切,图标,应用名,描述,入口页面等等等,然后再配置好你程序里需要用到的模块权限,按需配置 ...
- 《Mysql - 我的Mysql为什么会抖一下?》
一: 抖一下? - 平时的工作中,不知道有没有遇到过这样的场景. - 一条 SQL 语句,正常执行的时候特别快,但是有时也不知道怎么回事,它就会变得特别慢. - 并且这样的场景很难复现,它不只随机,而 ...
- malloc/free和new/delete详解与应用
C++面试经常会问到关于malloc/free和new/delete的区别,网上有不同版本的解释,这里总结下并加上个人理解和使用. 两者相同点 1.都可以申请动态堆内存. 两者不同点 1.new/de ...
- pandas中的argsort
直接通过例子看比较好理解. import pandas as pd data = [[1, 2, 3], [2, 2, 2], [7, 8, 9]] df = pd.DataFrame(data, i ...