java 深拷贝与浅拷贝机制详解
发布时间 - 2026-01-10 23:09:22 点击率:次java 深拷贝与浅拷贝机制详解

概要:
在Java中,拷贝分为深拷贝和浅拷贝两种。java在公共超类Object中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝。
(一)Object中clone方法
如果我们new出一个新对象,用一个声明去引用它,之后又用另一个声明去引用前一个声明,那么最后的结果是:这两个声明的变量将指向同一个对象,一处被改全部被改。如果我们想创建一个对象的copy,这个copy和对象的各种属性完全相同,而且修改这个copy和原对象毫无关系,那么这个时候我们就要用到clone方法。
package Clone;
import java.util.Date;
/**
*
* @author QuinnNorris
* java中的两种拷贝机制
*/
public class Clone {
/**
* @param args
* @throws CloneNotSupportedException
*/
public static void main(String[] args) throws CloneNotSupportedException {
// TODO Auto-generated method stub
ClassA valA = new ClassA(1, "old", new Date());
// 声明一个新的ClassA对象,我们不需要太关注ClassA的功能
ClassA valB = valA;
// 将valA引用的对象赋给valB
valA.setObject("new");
// 更改valA中的值,此时valB也被更改了,因为valA和valB指向同一个对象
valB = valA.clone();//通过clone方法制造副本
}
}
ClassA类中关于clone方法的重写部分:
//需要实现Cloneable接口
public class ClassA implements Cloneable {
public ClassA clone() throws CloneNotSupportedException {
return (ClassA) super.clone();//调用父类(Object)的clone方法
}
}
1.如何使用Object中clone方法的
有人总结使用clone方法的四条法则,我们一起分享一下:
- 为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
- 在派生类中覆盖基类的clone()方法,并声明为public。
- 在派生类的clone()方法中,调用super.clone()。
- 在派生类中实现Cloneable接口。
2.protected修饰的clone方法
在java.lang.Object的中,他将clone方法设置为protected修饰,这是很特殊的一种情况。protected的作用域是:包可见+可继承。之所以这样设置,是因为这个方法要返回的是克隆出来的对象,即clone方法要去克隆的类型是未知的,没有办法确定返回值的类型,自然只能让子孙后代来实现它重写它,为了能够让后代继承而又不过与张开,设置为了protected类型。
3.实现clone方法需要实现Cloneable接口
那么我们重写clone方法的时候为什么要去实现Cloneable接口呢?事实上,Cloneable接口是java中的一个标记接口,标记接口是指那些没有方法和属性的接口,他们存在只是为了让大家知道一些信息,而且在用:xxx instanceof Cloneable 的时候可以进行判断。Cloneable这个接口的出现就是为了让设计者知道要进行克隆处理了。如果一个对象需要克隆,但是没有实现(实际上,这里的“实现”换成“写上”更准确)Cloneable接口,那么会产生一个已检验异常。
4.实现clone方法需要调用父类的clone
我们为了达到复制一个和调用方法的这个对象一模一样的对象的目的,我们需要使用父类的clone方法,父类也以此类推,知道达到了Object的clone方法,那么Object的clone方法有什么用呢?API中是这样说的:
protected Object clone( ) throws CloneNotSupportedException
创建并返回此对象的一个副本。
“副本”的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象 x,
表达式: x.clone() != x为 true,
表达式: x.clone().getClass() == x.getClass()也为 true,
但这些并非必须要满足的要求。
一般情况下:
x.clone().equals(x)为 true,但这并非必须要满足的要求。
按照惯例,返回的对象应该通过调用 super.clone 获得。
如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()。
上面就是API中对clone的一部分基本讲解。我们可以得出结论的是,只要合理的调用了spuer.clone( )它就会返回一个被克隆的对象。在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。在这个克隆对象中,所有的属性都和被克隆的对象的属性相同,而这些相同的属性分为两种:
第一种 : 八大原始类型和不可变的对象(比如String)
第二种 : 其他类对象
对于第一种,clone方法将他们的值设置为原对象的值,没有任何问题。对于第二种,clone方法只是简单的将复制的新对象的引用指向原对象指向的引用,第二种的类对象会被两个对象修改。那么这个时候就涉及一个深浅拷贝的概念了。
(二)浅拷贝
浅拷贝:被拷贝对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
比如举个例子,一个类A中有另外一个类B类型的变量。在A重写clone函数调用super.clone的时候,创建的新对象和原来对象中的类B类型的变量是同一个,他们指向了同一个B的类型变量。如果在A中对B的变量做了修改,在新的拷贝出来的对象中B的变量也会被同样的修改。
请记住,直接调用super.clone实现的clone方法全部都是浅拷贝。
(三)深拷贝
深拷贝:被拷贝对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
通俗的说,如果说浅拷贝,开始的时候是两条线,如果在最后有一个其他类的变量,那么这两条线最后会合二为一,共同指向这变量,都能对他进行操作。深拷贝则是完完全全的两条线,互不干涉,因为他已经把所有的内部中的变量的对象全都复制一遍了。
深拷贝在代码中,需要在clone方法中多书写调用这个类中其他类的变量的clone函数。
(四)串行化深拷贝
在框架中,有的时候我们发现其中并没有重写clone方法,那么我们在需要拷贝一个对象的时候是如何去操作的呢?答案是我们经常会使用串行化方法,实现Serializable接口。
去寻找其他的方法来替代深拷贝也是无可奈何的事情,如果采用传统的深拷贝,难道你拷贝一个对象的时候向其中追无数层来拷贝完所有的对象变量么?先不谈这么做的时间消耗,仅仅是写这样的代码都会让人望而生畏。串行化深拷贝就是这样一个相对简单的方法。
把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做 “解冻”或者“回鲜(depicking)”过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此“腌成咸菜”的只是对象的一个拷贝,Java咸菜还可以回鲜。
上面是网上的专业解释,我也不在这里班门弄斧了。在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。
public Object deepClone()
{
//写入对象
ByteArrayOutoutStream 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());
}
虽然这种学院派的代码看起来很复杂,其实只是把对象放到流里,再拿出来。相比较分析判断无数的clone,这样简直是再简单不过了。这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象是否设成transient。
transient:一个对象只要实现了Serilizable接口,这个对象就可以被序列化(序列化是指将java代码以字节序列的形式写出,即我们上面代码前三行写入对象),Java的这种序列化模式为开发者提供了很多便利,可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个的所有属性和方法都会自动序列化。但是有种情况是有些属性是不需要序列号的,所以就用到这个关键字。只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
(五)总结
在实际的应用中,深拷贝和浅拷贝只是两个概念,不一定谁比谁好,要按照实际的工作来确定如何去拷贝一个对象。如果在数据库操作方面,为了取出一张表时不涉及其他的表,肯定需要使用浅拷贝,而在框架的Serializable中,虽然耗时,但是深拷贝是非常有必要的。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
# java
# 深拷贝与浅拷贝机制
# 拷贝机制
# java中关于深拷贝的几种方式总结
# Java中的深拷贝和浅拷贝介绍
# Java的深拷贝与浅拷贝的几种实现方式
# java实现文件拷贝的七种方式
# Java 如何优雅的拷贝对象属性
# java对象拷贝详解及实例
# java中三种拷贝方法举例总结
# 的是
# 序列化
# 重写
# 两种
# 串行化
# 类中
# 都是
# 第二种
# 不需要
# 是指
# 象中
# 其他的
# 要去
# 实现了
# 这样做
# 这个时候
# 写到
# 设置为
# 中对
# 第一种
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】
Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】
Linux系统命令中screen命令详解
高配服务器限时抢购:企业级配置与回收服务一站式优惠方案
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?
教你用AI润色文章,让你的文字表达更专业
如何批量查询域名的建站时间记录?
JavaScript Ajax实现异步通信
最好的网站制作公司,网购哪个网站口碑最好,推荐几个?谢谢?
uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址
如何确保FTP站点访问权限与数据传输安全?
手机网站制作与建设方案,手机网站如何建设?
Laravel如何创建和注册中间件_Laravel中间件编写与应用流程
国美网站制作流程,国美电器蒸汽鍋怎么用官方网站?
Python文件操作最佳实践_稳定性说明【指导】
Bootstrap整体框架之JavaScript插件架构
佛山网站制作系统,佛山企业变更地址网上办理步骤?
Laravel如何使用Passport实现OAuth2?(完整配置步骤)
如何在宝塔面板中创建新站点?
javascript日期怎么处理_如何格式化输出
深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?
Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】
Android仿QQ列表左滑删除操作
高端网站建设与定制开发一站式解决方案 中企动力
Android GridView 滑动条设置一直显示状态(推荐)
怎么用AI帮你设计一套个性化的手机App图标?
Laravel如何保护应用免受CSRF攻击?(原理和示例)
Laravel如何实现用户角色和权限系统_Laravel角色权限管理机制
如何在IIS管理器中快速创建并配置网站?
Laravel如何配置任务调度?(Cron Job示例)
Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】
高防服务器如何保障网站安全无虞?
Laravel事件监听器怎么写_Laravel Event和Listener使用教程
Laravel Eloquent模型如何创建_Laravel ORM基础之Model创建与使用教程
香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南
Laravel如何处理CORS跨域问题_Laravel项目CORS配置与解决方案
Laravel如何实现用户密码重置功能?(完整流程代码)
如何快速搭建安全的FTP站点?
Laravel怎么定时执行任务_Laravel任务调度器Schedule配置与Cron设置【教程】
JavaScript模板引擎Template.js使用详解
Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作
详解CentOS6.5 安装 MySQL5.1.71的方法
Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面
Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】
Java类加载基本过程详细介绍
韩国服务器如何优化跨境访问实现高效连接?
湖南网站制作公司,湖南上善若水科技有限公司做什么的?
如何快速上传自定义模板至建站之星?
海南网站制作公司有哪些,海口网是哪家的?

