Java对象的克隆和深浅问题
Java实现克隆的方式
Java实现克隆的方式有如下两种, 推荐采用实现Cloneable
接口的方式
- 实现
Cloneable
接口, 重写clone方法, 调用父类的clone方法 - 还有另一种方法, 不实现
Cloneable
接口, 但是重写了clone方法, 调用了父类clone方法, 也可以实现克隆能力
Cloneable
接口和Serializable
接口一样, 是一个声明式接口, 无需重写其中的方法, 当某个类实现了此类接口时, 标明具备了某种能力, 如序列化和可克隆性.
Object的clone和重写
在根类Object
之中, 已经有了clone()方法 , 但是我们自己创建的类, 一般而言, 都是没有clone能力的, 因为默认是关闭了克隆能力的, 为何呢? 因为Java原本是设计用来控制硬件的语言, 但是当网络发展后, 安全性的问题开始出现, 而我们不可能允许一些敏感信息被随意clone, 但是原本的Object
之中已经具备了" 克隆"方法和能力, 所以在Java后续的发展之中, 采用的是补丁式的解决方式, 首先将Object
之中原来的public Object clone()
改写为protected Object clone()
, 然后规定每个类要实现克隆能力的话, 必须定义自己的clone()
方法, 随着发展, 又出现了Cloneable
接口, 作为一种声明式接口, 标明实现了Cloneable
接口的方法都具备克隆的能力, 当然也需要重写自己的clone()方法. 当我们不去对一个类做任何操作, 而去clone的时候, 会返回如下的错误, 表示此类没有开启克隆的能力, 所以必须重写自己的clone方法
java.lang.CloneNotSupportedException: com.cqu.zgy.syntax.clone.ClassForClone
克隆能力的开启关闭方式
默认情况下, 类的克隆能力是关闭的, 我们可以根据需要, 开启或者关闭, 开启和关闭的方法如下
开启class克隆能力的方式:
- 实现
Cloneable
接口,重写clone方法,调用父类的clone方法 - 还有另一种方法, 不实现
Cloneable
接口, 但是重写了clone方法, 调用了父类clone方法, 也可以实现克隆能力
- 实现
关闭class克隆能力的方式:
- 直接不管(默认是关闭的)
- 设置类为final
- 不去实现
Cloneable
接口 - 实现了
Cloneable
接口, 但是不去重写clone方法 - 实现了
Cloneable
接口, 然后在重写了的clone方法之中直接抛出异常
克隆的层次性
类的克隆能力是无法继承的, 所有的类, 当默认情况下, 都是默认关闭克隆能力的, 我们需要按照以上开启克隆能力的方式, 通过显式操作, 开启克隆能力, 此外, 如果子类继承自父类, 其克隆的能力也是要自行开启, 而无法通过继承实现. 父类可以clone, 子类不可clone, 子类不继承父类的克隆能力, 实现例子如下:
/*层次化的实现克隆, 在某一层开始具备克隆能力, 下面的都具有, 或者实现某一层停止克隆能力*/
/*继承关系之中, 继承的能力不可以继承, 某个类如果想要有『克隆』的能力, 就需要此类直接实现Cloneable, 重写clone, 调用父类clone*/
@Slf4j
public class LayeringClone {
public static void main(String[] args) {
School sc = new School("中央大学");
College co = new College("日本语");
Department de1 = new Department("商务日语", 1);
Department de2 = new Department("日本文学", 2);
SClass sc1 = new SClass("商务1班", 1, 40);
//School不可克隆
//School scClone=sc.clone();
//College可以克隆
try {
College coClone = (College) co.clone();
System.out.println(co.getInfo());
System.out.println(coClone.getInfo());
} catch (CloneNotSupportedException e) {
log.error("College克隆发生异常!", e);
}
//Department可以克隆
try {
Department deClone = (Department) de1.clone();
System.out.println(de1.getInfo());
System.out.println(deClone.getInfo());
} catch (CloneNotSupportedException e) {
log.error("Department克隆发生异常!", e);
}
//SClass不可以克隆, 因为没有实现Cloneable接口
try {
SClass scClone = (SClass) sc1.clone();
System.out.println(sc1.getInfo());
System.out.println(scClone.getInfo());
Department scc = new SClass("scc");
System.out.println(scc.getClass());//父类可以clone, 子类不可clone, 子类不继承父类的克隆能力
SClass scClone2 = (SClass)((Department) scc).clone();//父类可以clone, 子类不可clone, 子类不继承父类的克隆能力
} catch (CloneNotSupportedException e) {
log.error("SClass克隆发生异常!", e);
}
}
}
/*学校, 不可克隆, 未实现Cloneable*/
@Data
class School {
private String name;
public School() {
}
public School(String name) {
this.name = name;
}
public String getInfo() {
return "School name is: " + this.getName();
}
}
/*学院, 可以克隆, 实现Cloneable*/
@Data
class College extends School implements Cloneable {
private String name;
public College() {
}
public College(String name) {
super(name);
this.name = name;
}
@Override
public String getInfo() {
return "College name is: " + this.getName();
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/*专业方向, 可以克隆, 实现Cloneable*/
@Data
class Department extends College implements Cloneable {
private String name;
private int depNo;
public Department() {
}
public Department(String name) {
super(name);
this.name = name;
}
public Department(String name, int depNo) {
super(name);
this.name = name;
this.depNo = depNo;
}
@Override
public String getInfo() {
return "Department name is: " + this.getName() + ", Department depNo is: " + this.getDepNo();
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/*班级, 不可克隆, 实现Cloneable*/
@Data
class SClass extends Department implements Cloneable {
private String name;
private int id;
private int stuNo;
public SClass() {
}
public SClass(String name) {
super(name);
this.name = name;
}
public SClass(String name, int id, int stuNo) {
super(name);
this.name = name;
this.id = id;
this.stuNo = stuNo;
}
@Override
public String getInfo() {
return "SClass name is: " + this.getName() + ", SClass id is: " + this.getId()
+ ", SClass stuNo is: " + this.getStuNo();
}
//通过操作clone函数,抛出一个异常,而主动停止clone的能力
@Override
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("不支持克隆...");
}
}
==和equals
在Java之中, 同一个对象肯定是地址相同, 而内容相同的对象则是equal的. 也就是 (地址) 和 equals(内容) 的区别. Java之中, ==对于对象类型而言, 比较的是地址, 而非内容. 比较内容需要使用equals(). 赋值例如Object a = new Object(); Object b = a;
a赋值给b, b对象作为a对象的一个别名, 其地址和内容都是相等的 也就是 ab 和a.equals(b) 都返回的是true; 两个对象(对象之中的字段都是基本类型的情况), a clone自b, 此时a和b的内容是相等的, 但是a和b的地址是不相等的, 因为其是两个对象, 在JVM之中分配了不同的堆, 所以ab: false, 而a.equals(b): true, 在克隆之中, 我们可以使用 和equals的关系来测试深克隆和浅克隆.
深克隆和浅克隆
克隆就是复制, 复制要求对象的内容是一致的, 这就叫做复制, 深克隆与浅克隆的区别就是: 浅克隆不会克隆原对象中的引用类型, 仅仅拷贝了引用类型的指向. 深克隆则拷贝了对象之中的所有属性和对象, 也就是说深克隆能够做到原对象和新对象之间完全没有影响. 而深克隆的实现就是在引用类型所在的类实现Cloneable
接口, 并使用public访问修饰符重写clone方法, 浅克隆重写clone方法的时候,只需要调用父类的clone方法即可, 而深克隆, 则需要每一层都调用其上一层的clone方法, 并且返回当前的对象.
对象类型:
a.一个对象之中的字段有基本类型String, int
b.一个对象之中的字段有基本类型String, int, Country(国家类,一个自己创建的复杂的类)
1.浅克隆
对于 a 类型, 只有简单的基本类型字段的时候, 那么就深克隆和浅克隆的效果是一样的
对于 b 类型, 当浅克隆的时候, 此对象和克隆对象的地址不相同, 但是内容相同, 而Country对象, 克隆前后两个对象使用的是同一个, ==, equals都为true
2.深克隆
对于 a 类型, 只有简单的基本类型字段的时候, 那么就深克隆和浅克隆的效果是一样的
对于 b 类型, 当深克隆的时候, 此对象和克隆对象的地址不相同, 内容相同, 而Country对象, 克隆前后两个对象使用的也是两个对象, == 为false, equals为true
深克隆和浅克隆的使用例子如下:
/*深克隆和浅克隆*/
public class ShadowDeepClone {
/**
* 说明了如果想要克隆某一个对象, 那么它的类, 要实现了Cloneable接口, 重写了clone方法, 调用父类方法可以实现克隆能力
* 还有另一种方法, 不实现Cloneable接口, 但是重写了clone方法, 调用了父类clone方法, 也可以实现克隆能力
**/
public static void testNoCloneableWithCloneMethod() throws CloneNotSupportedException {
Tibet t1 = new Tibet("西藏藏语", "西藏");
System.out.println(t1.getInfo());
Tibet t2 = (Tibet) t1.clone();
System.out.println(t2.getInfo());
}
public static void testShadowClone() throws CloneNotSupportedException {
Yu yu1 = new Yu("河南方言郑州话", "中国", 140000000);
Yu yu2 = new Yu("河南方言安徽北部", "中国", 7006600);
BeifangDialect bf1 = new BeifangDialect("北方方言河南郑州话", yu1);
BeifangDialect bf2 = new BeifangDialect("北方方言河南安徽北部话", yu2);
BeifangDialect bf1Clone = (BeifangDialect) bf1.clone();
System.out.println(bf1.getInfo());
System.out.println(bf1Clone.getInfo());
System.out.println("bf1.equals(bf1Clone): " + (bf1.equals(bf1Clone)));
System.out.println("bf1 == bf1Clone: " + (bf1 == bf1Clone));
System.out.println("bf1.getYu().equals(bf1Clone.getYu()): " + (bf1.getYu().equals(bf1Clone.getYu())));
System.out.println("bf1.getYu() == bf1Clone.getYu(): " + (bf1.getYu() == bf1Clone.getYu()));
System.out.println("说明bf1之中的Yu和bf1Clone的Yu对象是同一个, 而bf1和bf1Clone是两个对象");
}
public static void testDeepClone() throws CloneNotSupportedException {
Wu wu = new Wu("吴方言", "中国", "环太湖地区");
JiangnanDialect jnSuzhou = new JiangnanDialect("苏州方言", wu);
JiangnanDialect jnKunshan = (JiangnanDialect) jnSuzhou.clone();
System.out.println(jnSuzhou.getInfo());
System.out.println(jnKunshan.getInfo());
System.out.println("jnSuzhou.equals(jnKunshan): " + (jnSuzhou.equals(jnKunshan)));
System.out.println("jnSuzhou == jnKunshan: " + (jnSuzhou == jnKunshan));
System.out.println("jnSuzhou.getWu().equals(jnKunshan.getWu()): " + (jnSuzhou.getWu().equals(jnKunshan.getWu())));
System.out.println("jnSuzhou.getWu() == jnKunshan.getWu(): " + (jnSuzhou.getWu() == jnKunshan.getWu()));
System.out.println("说明jnSuzhou之中的Wu和jnKunshan的Wu对象[[[不是同一个]]], 并且jnSuzhou和jnKunshan是两个对象, 但是内容相等");
}
public static void main(String[] args) throws CloneNotSupportedException {
System.out.println("第二种实现克隆的方法...");
testNoCloneableWithCloneMethod();
//浅克隆, 只是克隆表层的对象, 对对象之中的对象不复制
System.out.println("\n\n浅克隆...");
testShadowClone();
//深克隆, 克隆整个的对象, 对对象之中的对象也要复制
System.out.println("\n\n深克隆...");
testDeepClone();
}
}
//江南方言, 深克隆
@Data
class JiangnanDialect implements Cloneable {
private String name;
private Wu wu;
JiangnanDialect(String name, Wu wu) {
this.name = name;
this.wu = wu;
}
public String getInfo() {
return "Language name is: " + this.getName() + ", [[[具体小语种 : " + this.getWu().getInfo()
+ "]]], Class name is: " + this.getClass().getName();
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 在clone方法之中, 完成了本类层面的字段属性的clone, 从而实现了完整的克隆;
// 但是如果有多层, 每一层都要嵌套, 才能实现深克隆, 不然仍然是浅克隆
JiangnanDialect jn = (JiangnanDialect) super.clone();
jn.wu = (Wu) wu.clone();
return jn;
}
}
//北方方言, 浅克隆
@Data
class BeifangDialect implements Cloneable {
private String name;
private Yu yu;
BeifangDialect(String name, Yu yu) {
this.name = name;
this.yu = yu;
}
public String getInfo() {
return "Language name is: " + this.getName() + ", [[[具体小语种 : " + this.getYu().getInfo()
+ "]]], Class name is: " + this.getClass().getName();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//下面是语言的一些类
/*Language可以克隆*/
@Data
class Language implements Cloneable {
private String name;//名称
Language() {
}
Language(String name) {
this.name = name;
}
public String getInfo() {
return "Language name is: " + this.getName() + ", Class name is: " + this.getClass().getName();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/*Han可以克隆*/
@Data
class Han extends Language implements Cloneable {
private String name;
private String country;//国家
Han() {
super();
}
Han(String name, String country) {
super(name);
this.name = name;
this.country = country;
}
@Override
public String getInfo() {
return "Language name is: " + this.getName() + ", Country name is: " + this.getCountry()
+ ", Class name is: " + this.getClass().getName();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/*Wu可以克隆*/
@Data
class Wu extends Han implements Cloneable {
private String name;
private String country;
private String region;//地区
Wu() {
super();
}
Wu(String name, String country, String region) {
super(name, country);
this.name = name;
this.country = country;
this.region = region;
}
@Override
public String getInfo() {
return "Language name is: " + this.getName() + ", Country name is: " + this.getCountry()
+ ", Region name is: " + this.getRegion() + ", Class name is: " + this.getClass().getName();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/*Yu不可以克隆*/
@Data
class Yu extends Han {
private String name;
private String country;
private int population;//人口数量
Yu() {
super();
}
Yu(String name, String country, int population) {
super(name, country);
this.name = name;
this.country = country;
this.population = population;
}
@Override
public String getInfo() {
return "Language name is: " + this.getName() + ", Country name is: " + this.getCountry()
+ ", population is: " + this.getPopulation() + ", Class name is: " + this.getClass().getName();
}
}
/*Tibet克隆, 测试一下其未实现Cloneable接口, 但是实现了clone方法的重写, 看其是否可以克隆*/
@Data
class Tibet extends Language {
private String name;//名称
private String region;//地区
Tibet() {
}
Tibet(String name, String region) {
super(name);
this.name = name;//调用了super(name), 但还是需要使用this.name=name; 为name赋值, 否则name为null
this.region = region;
}
@Override
public String getInfo() {
return "Language name is: " + this.getName() + ", Region name is: " + this.getRegion()
+ ", Class name is: " + this.getClass().getName();
}
// 未实现Cloneable接口, 但是实现了clone方法重写
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
ref:
1.Java的clone():深复制与浅复制, 2.Java对象克隆——浅克隆和深克隆的区别, 3.java Clone使用方法详解
Java对象的克隆和深浅问题的更多相关文章
- 【JAVA零基础入门系列】Day14 Java对象的克隆
今天要介绍一个概念,对象的克隆.本篇有一定难度,请先做好心理准备.看不懂的话可以多看两遍,还是不懂的话,可以在下方留言,我会看情况进行修改和补充. 克隆,自然就是将对象重新复制一份,那为什么要用克隆呢 ...
- java对象 深度克隆(不实现Cloneable接口)和浅度克隆
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt128 为什么需要克隆: 在实际编程过程中,我们常常要遇到这种情况:有一个对象 ...
- Java对象的克隆
今天要介绍一个概念,对象的克隆.本篇有一定难度,请先做好心理准备.看不懂的话可以多看两遍,还是不懂的话,可以在下方留言,我会看情况进行修改和补充. 克隆,自然就是将对象重新复制一份,那为什么要用克隆呢 ...
- java对象的克隆以及深拷贝与浅拷贝
一.为什么要使用克隆 在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也 ...
- Java的“影子克隆”和“深度克隆”
今天来学习学习java对象的克隆,在写代码的时候,有时候我们会这样写:对象1=对象2,也就是把对象2赋值给对象1了,但是这样做有个问题,就是如果我们修改了对象2的属性值,对象1的相同属性值也被修改了, ...
- java对象比较器和克隆
一.比较器Comparable和Comparator 上一篇博客介绍了工具类Arrays工具类 .我们可以对基本类型的数组调用Arrays.sort()函数来进行数组的排序.排序操作在日常开发中经常要 ...
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
Java对象克隆(Clone)及Cloneable接口.Serializable接口的深入探讨 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的克隆,就不得不说为什么 ...
- java 的对象拷贝(有深浅拷贝两种方式,深拷贝实现的两种方式(逐层实现cloneable接口,序列化的方式来实现))
Java提高篇--对象克隆(复制)(转自:http://www.cnblogs.com/Qian123/p/5710533.html#_label0) 阅读目录 为什么要克隆? 如何实现克隆 浅克 ...
- (转)Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
原文地址:http://blog.csdn.net/kenthong/article/details/5758884 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的 ...
随机推荐
- File初识和练习
目录 File类 File对象的构建 File文件名.路径的获取 文件的状态 文件的其他操作 创建文件夹 列出下一级 实战练习1:列出子孙级目录及名称 实战练习2:列出文件及其子孙文件的总大小 实战练 ...
- Lonsdor K518ISE SCION 2011-2018 Models Enabled!
Lonsdor released the Lonsdor K518ISE Key Programmer update announcement on 14-03-2019, saying it can ...
- Android自动化之Monkey环境搭建(一)
从事测试行业两年了,一直很喜欢研究新技术,但是最近有点慵懒.正好公司新出了产品,督促我学习monkey用来测其稳定性. 网上搜索了很久,内容总是很零散,通常需要找几篇文章才能搭好环境.特写此文,一篇文 ...
- c语言知识
1. 指针 https://blog.csdn.net/lwbeyond/article/details/6180640 http://www.cnblogs.com/lvyahui/p/696528 ...
- 如何实现Activiti的分支条件的自定义配置(转)
如何实现Activiti的分支条件的自定义配置 博客分类: Activiti Java SaaS 一.Activiti的流程分支条件的局限 Activiti的流程分支条件目前是采用脚本判断方式,并 ...
- System.Web.Caching.Cache缓存帮助类
/// <summary> /// 缓存帮助类 /// </summary> public class CacheHelper { /// <summary> // ...
- WCF调错方法
1.在VS cmd里,输入wcftestclient.exe 2.添加Service服务. 3.点击要测试的方法,输入参数,点击Invoke. 4.如果错误信息很模糊,则修改WCF程序所在的Web.c ...
- 【Selenium】【BugList5】chrom窗口未关闭,又新开窗口,报错:[8564:8632:0522/111825.341:ERROR:persistent_memory_allocator.cc(845)] Corruption detected in shared-memory segment.
环境信息:Windows7 64位 + python 3.6.5 + selenium 3.11.0 +pyCharm 解决方法: 执行 driver = webdriver.Chrome()前必须把 ...
- python的语法小结之生成器和迭代器
生成器: 首先介绍一下列表生成式:a=[x for x in range(10)] >>>>>>[0, 1, 2, 3, 4, 5, 6 ...
- python的语法小结
break 与continue的区别: 1.break是直接中断全部循环 2.continue则是在只不执行此次所循环的东西,其它循环依旧执行,比方说只是跳过第4次循环,第5次循环照常进行. \n 表 ...