浅谈Java中的深拷贝和浅拷贝(转载)

Java:Java几种深拷贝方式比较 1)、序列化性能 Clone > new > Kryo序列化 > Jdk序列化 > Json(各种Json类似)序列化2)、Clone深拷贝性能最高,但是如果属性中有特定的对象字段,则需要自己编写代码3)、new 性能仅次于Clone,因为需要执行Jvm过程(常量池判断,内存分配,值初始化,init方法调用,栈中对象的引用等),并且主要是每个对象需要单独编写代码,当然也不建议使用反射4)、kryo 性能较高,并且不需要单独的开发, 若对性能不是特别高,可以考虑使用. 阅读详情

浅谈Java中的深拷贝和浅拷贝(转载)


原文链接:

http://blog.youkuaiyun.com/tounaobun/article/details/8491392

假如说你想复制一个简单变量。很简单:



int apples = 5;
int pears = apples;
int apples = 5;
int pears = apples;


不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。



但是如果你复制的是一个对象,情况就有些复杂了。



假设说我是一个beginner,我会这样写:



class Student {
private int number;

public int getNumber() {
return number;
}

public void setNumber(int number) {
this.number = number;
}

}
public class Test {

public static void main(String args[]) {

Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = stu1;

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
}
}
class Student {
private int number;

public int getNumber() {
return number;
}

public void setNumber(int number) {
this.number = number;
}

}
public class Test {

public static void main(String args[]) {

Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = stu1;

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
}
}




打印结果:



学生1:12345
学生2:12345
学生1:12345
学生2:12345


这里我们自定义了一个学生类,该类只有一个number字段。

我们新建了一个学生实例,然后将该值赋值给stu2实例。(Student stu2 = stu1;)

再看看打印结果,作为一个新手,拍了拍胸腹,对象复制不过如此,

难道真的是这样吗?



我们试着改变stu2实例的number字段,再打印结果看看:



stu2.setNumber(54321);

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
stu2.setNumber(54321);

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
打印结果:





学生1:54321
学生2:54321
学生1:54321
学生2:54321
这就怪了,为什么改变学生2的学号,学生1的学号也发生了变化呢?





原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,

这样,stu1和stu2指向内存堆中同一个对象。如图:







那么,怎样才能达到复制一个对象呢?

是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。

该方法的签名是:

protected native Object clone() throws CloneNotSupportedException;



因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。

要想对一个对象进行复制,就需要对clone方法覆盖。



一般步骤是(浅复制):

1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)

2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)



下面对上面那个方法进行改造:



class Student implements Cloneable{
private int number;

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();
}
return stu;
}
}
public class Test {

public static void main(String args[]) {

Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = (Student)stu1.clone();

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());

stu2.setNumber(54321);

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
}
}
class Student implements Cloneable{
private int number;

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();
}
return stu;
}
}
public class Test {

public static void main(String args[]) {

Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = (Student)stu1.clone();

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());

stu2.setNumber(54321);

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
}
}
打印结果:





学生1:12345
学生2:12345
学生1:12345
学生2:54321
学生1:12345
学生2:12345
学生1:12345
学生2:54321
如果你还不相信这两个对象不是同一个对象,那么你可以看看这一句:





System.out.println(stu1 == stu2); // false
System.out.println(stu1 == stu2); // false


上面的复制被称为浅复制(Shallow Copy),还有一种稍微复杂的深度复制(deep copy):

我们在学生类里再加一个Address类。



class Address {
private String add;

public String getAdd() {
return add;
}

public void setAdd(String add) {
this.add = add;
}

}

class Student implements Cloneable{
private int number;

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();
}
return stu;
}
}
public class Test {

public static void main(String args[]) {

Address addr = new Address();
addr.setAdd("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
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());
}
}
class Address {
private String add;

public String getAdd() {
return add;
}

public void setAdd(String add) {
this.add = add;
}

}

class Student implements Cloneable{
private int number;

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();
}
return stu;
}
}
public class Test {

public static void main(String args[]) {

Address addr = new Address();
addr.setAdd("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
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());
}
}
打印结果:





学生1:123,地址:杭州市
学生2:123,地址:杭州市
学生1:123,地址:杭州市
学生2:123,地址:杭州市
乍一看没什么问题,真的是这样吗?



我们在main方法中试着改变addr实例的地址。



addr.setAdd("西湖区");

System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
addr.setAdd("西湖区");

System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
打印结果:





学生1:123,地址:杭州市
学生2:123,地址:杭州市
学生1:123,地址:西湖区
学生2:123,地址:西湖区
学生1:123,地址:杭州市
学生2:123,地址:杭州市
学生1:123,地址:西湖区
学生2:123,地址:西湖区
这就奇怪了,怎么两个学生的地址都改变了?



原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。

所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:





package abc;

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;
}
}

class Student implements Cloneable{
private int number;

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 Test {

public static void main(String args[]) {

Address addr = new Address();
addr.setAdd("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
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());

addr.setAdd("西湖区");

System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
}
}
package abc;

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;
}
}

class Student implements Cloneable{
private int number;

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 Test {

public static void main(String args[]) {

Address addr = new Address();
addr.setAdd("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
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());

addr.setAdd("西湖区");

System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
}
}
打印结果:





学生1:123,地址:杭州市
学生2:123,地址:杭州市
学生1:123,地址:西湖区
学生2:123,地址:杭州市
学生1:123,地址:杭州市
学生2:123,地址:杭州市
学生1:123,地址:西湖区
学生2:123,地址:杭州市
这样结果就符合我们的想法了。

总结:浅拷贝是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝,

没有对引用指向的对象进行拷贝。

而深拷贝是指在拷贝对象时,同时会对引用指向的对象进行拷贝。

区别就在于是否对 对象中的引用变量所指向的对象进行拷贝。



最后我们可以看看API里其中一个实现了clone方法的类:

java.util.Date:



/**
* Return a copy of this object.
*/
public Object clone() {
Date d = null;
try {
d = (Date)super.clone();
if (cdate != null) {
d.cdate = (BaseCalendar.Date) cdate.clone();
}
} catch (CloneNotSupportedException e) {} // Won't happen
return d;
}
/**
* Return a copy of this object.
*/
public Object clone() {
Date d = null;
try {
d = (Date)super.clone();
if (cdate != null) {
d.cdate = (BaseCalendar.Date) cdate.clone();
}
} catch (CloneNotSupportedException e) {} // Won't happen
return d;
}


该类其实也属于深度复制。
算法竞赛推荐书单 前言 入坑算法竞赛已经一年半了,在这期间也读了不少关于算法竞赛的书,这些书对我的帮助还是蛮大的。平时看题解多是个人零散的题解,很难以系统的去学习,认认真真的挑选1-2本书来阅读,无论是从思维能力还是文字表达能力都会得到提升,以下都是我自己读过的书进行一下分享,难度由浅入深,部分书籍浅尝而止。 入门:《啊哈!算法》 适合:总题量150以下初步掌握C语言的萌新 一本充满智慧和趣味的算法入门书。没有枯燥的描述,没有难懂的公式,一切以实际应用为出发点,通过幽默的语言配以可爱的插图来讲解算法。你更像是在阅读一个个轻松 阅读详情

相关推荐

Pcileech(DMA)开源项目简述

希望大DMA家能正确认识FPGA,正确使用DMA技术,对于市场上各类伪装成花里胡哨设备的FPGA板卡它的本质是不会变的,不要上当受骗。

2301_79773847的博客 5960

Java对象复制

Java对象复制(拷贝)

骑个小蜗牛的博客 5万+

SAP ALV表格F4搜索帮助不触发DATA_CHANGE?3步排查搞定

本文针对SAP ALV表格中F4搜索帮助选择值后,DATA_CHANGE事件不触发的经典问题,提供了三步系统性排查方法。核心在于理解F4值回写机制,并需在事件处理中手动填充修改表以通知ALV控件数据变更,从而确保后续校验、自动计算等依赖DATA_CHANGE的逻辑能正常执行。

c2d3e4f的博客 1132

Java对象的复制四种方式

在这里,由于面试中MySQL问的比较多,因此也就在此以MySQL为例为大家总结分享。但是你要学习的往往不止这一点,还有一些主流框架的使用,Spring源码的学习,Mybatis源码的学习等等都是需要掌握的,我也把这些知识点都整理起来了《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!a获取)**在这里,由于面试中MySQL问的比较多,因此也就在此以MySQL为例为大家总结分享。

2303_77877769的博客 4688

Python高效转换病理图像:从mrxs到SVS的完整实践指南

本文提供了使用Python将数字病理切片从mrxs格式高效转换为SVS格式的完整实践指南。针对mrxs格式兼容性差的问题,文章详细解析了基于openslide和tifffile库的核心转换流程,并重点分享了应对大内存消耗的优化方案与流式处理技巧,确保转换后的SVS文件保留多分辨率金字塔结构,兼容主流病理分析工具。

weixin_29051811的博客 547

VSCode多文件C语言项目编译实战:从配置到运行

本文详细讲解了如何在VSCode中配置多文件C语言项目的编译环境。通过手把手教学,指导读者创建项目结构、编写代码,并重点深度配置task.json与launch.json文件,实现一键编译和调试所有C文件,解决常见的“undefined reference”等链接错误,提升开发效率。

g8f9d0s1a2的博客 1098

Java对象复制(直接赋值,浅拷贝,深拷贝)

可以发现原对象user1中的Student引用类型对应的值改变后,拷贝对象user2中的student值也跟着变了,说明拷贝后的对象中的引用类型与原对象的引用是同一个。在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝),写到一个流里,再从流里读出来,便可以重建对象。原user1对象中student引用的实例值改变了,拷贝后的对象中引用的实例值没有变,说明它们两个不是同一个引用。创建一个新对象,然后将当前对象的非静态字段复制到该对象,

weixin_44924882的博客 2万+

用ESP32-S3和USB摄像头DIY一个无线图传监控(附完整代码和接线图)

本文详细介绍了如何利用ESP32-S3开发板和USB摄像头打造一套高性价比的无线图传监控系统。从硬件选型、接线图到代码实现,提供了完整的DIY方案,特别适合家庭监控、宠物看护等场景。通过优化WiFi传输和双核任务分配,实现了稳定的视频流传输,整套方案成本控制在200元以内。

weixin_33701251的博客 519

Java 对象复制 Java对象复制6种情况 效率对比 BeanUtils 对象复制 Dozer 复习复制 MapStruct对象复制

在Java开发中,经常会遇到对象的复制转换操作 1、Spring BeanUtils 2、Apache BeanUtils 3、Dozer 4、Hutool BeanUtil 5、cglib BeanCopier 6、MapStruct

HaHa_Sir的博客 1万+

硬件工程师笔记——电子器件汇总大全

本文系统总结了14种常用电子器件的工作原理及功能应用,包括电阻、压敏电阻、电容、电感、二极管、三极管、MOS管、晶振、光耦、IGBT、继电器、磁珠、可控硅和线性稳压器。每种器件从结构原理、工作特性到典型应用进行全面解析,如电阻的限流分压、电容的储能滤波、三极管的放大开关等。重点阐述了各器件在电路设计中的关键作用,如IGBT的功率控制、继电器的隔离保护、LDO的电压稳定等,并提供了实际电路应用案例。文章还整理了硬件工程师笔试面试相关的知识点和真题资源,为电子工程师提供全面的技术参考。

XU157303764的博客 2万+

NSX-T 4.1部署搭建

NSX-T4.1 部署搭建

weixin_58250887的博客 564

java 深拷贝 流_浅谈Java中的深拷贝和浅拷贝(转载)

浅谈Java中的深拷贝和浅拷贝(转载)原文链接:假如说你想复制一个简单变量。很简单:int apples = 5;int pears = apples;int apples = 5;int pears = apples;不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。但是如果你复制的是一个对象,情况就...

weixin_42367947的博客 168

基于Python爬虫+词云图+情感分析对某东上完美日记的用户评论分析

随着互联网和社交媒体的发展,用户评论成为了消费者表达自己意见和情感的主要途径之一。对于企业来说,深入了解用户对其产品或服务的看法可以帮助他们更好地了解市场需求、产品改进的方向,以及消费者的情感倾向。因此,对用户评论进行分析已经成为了市场研究和商业决策的重要手段之一。

m0_64336780的博客 1万+

PhysX 5 源码尽调:从架构拆解到 Omniverse 集成与企业级二次开发

物理引擎是数字孪生与工业仿真底座的基石,NVIDIA PhysX 凭借其卓越的刚体动力学、碰撞检测与 GPU 加速能力,已成为 Omniverse 物理仿真的核心引擎。理解 PhysX 的源码架构、仿真流水线(BroadPhase、NarrowPhase、Solver)以及 TGS 求解器、CCD 连续碰撞检测等关键机制,有助于开发者从原理层面掌握物理仿真性能调优的底层逻辑。PhysX 5 采用 CMake 构建,模块化与 SIMD 优化遍布核心代码,其 Source Available 许可证与 Omni

weixin_34099526的博客 284

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

浅谈Java中的深拷贝和浅拷贝

  浅谈Java中的深拷贝和浅拷贝(转载) 原文链接: http://blog.youkuaiyun.com/tounaobun/article/details/8491392 假如说你想复制一个简单变量。很简单: [java]view plaincopyprint? int apples =5; int pears = apples; int a...

aizai2266的博客 99

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

java浅拷贝与深拷贝

浅谈Java中的深拷贝和浅拷贝(转载) 原文链接:http://blog.youkuaiyun.com/tounaobun/article/details/8491392假如说你想复制一个简单变量。很简单:int apples = 5; int pears = apples; int apples = 5; int pears = apples; ``` 不仅仅是in

jjlanbupt的博客 414

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

浅谈BeanUtils的拷贝,深度克隆

转载自:https://www.cnblogs.com/tison/p/7840647.html 1、BeanUtil本地简单测试在项目中由于需要对某些对象进行深度拷贝然后进行持久化操作,想到了apache和spring都提供了BeanUtils的深度拷贝工具包,自己写了几个Demo做测试,定义了两个类User和Person,其中User的属性引用了Person类。 publ...

weixin_30755393的博客 176

浅谈Javascript 中几种克隆(clone)方式

转载:https://www.cnblogs.com/zouhao/p/7278117.html 一:在Javascript里,如果克隆对象是基本类型,我们直接赋值就可以了: 1 var sStr = "kingwell"; 2 var cStr = sStr; 3 alert(cStr);//输出kingwell 4 sStr = "abc"; 5 alert(cS...

bangwencai0604的博客 169
上一篇: 在new 控件时LayoutParams的使用
下一篇: scrollview嵌套gridview:scrollview不是置顶显示
安卓鸟
博客等级 码龄15年 14粉丝 36原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值