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对象的克隆和深浅问题的更多相关文章

  1. 【JAVA零基础入门系列】Day14 Java对象的克隆

    今天要介绍一个概念,对象的克隆.本篇有一定难度,请先做好心理准备.看不懂的话可以多看两遍,还是不懂的话,可以在下方留言,我会看情况进行修改和补充. 克隆,自然就是将对象重新复制一份,那为什么要用克隆呢 ...

  2. java对象 深度克隆(不实现Cloneable接口)和浅度克隆

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt128 为什么需要克隆: 在实际编程过程中,我们常常要遇到这种情况:有一个对象 ...

  3. Java对象的克隆

    今天要介绍一个概念,对象的克隆.本篇有一定难度,请先做好心理准备.看不懂的话可以多看两遍,还是不懂的话,可以在下方留言,我会看情况进行修改和补充. 克隆,自然就是将对象重新复制一份,那为什么要用克隆呢 ...

  4. java对象的克隆以及深拷贝与浅拷贝

    一.为什么要使用克隆 在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也 ...

  5. Java的“影子克隆”和“深度克隆”

    今天来学习学习java对象的克隆,在写代码的时候,有时候我们会这样写:对象1=对象2,也就是把对象2赋值给对象1了,但是这样做有个问题,就是如果我们修改了对象2的属性值,对象1的相同属性值也被修改了, ...

  6. java对象比较器和克隆

    一.比较器Comparable和Comparator 上一篇博客介绍了工具类Arrays工具类 .我们可以对基本类型的数组调用Arrays.sort()函数来进行数组的排序.排序操作在日常开发中经常要 ...

  7. Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

    Java对象克隆(Clone)及Cloneable接口.Serializable接口的深入探讨 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的克隆,就不得不说为什么 ...

  8. java 的对象拷贝(有深浅拷贝两种方式,深拷贝实现的两种方式(逐层实现cloneable接口,序列化的方式来实现))

    Java提高篇--对象克隆(复制)(转自:http://www.cnblogs.com/Qian123/p/5710533.html#_label0)   阅读目录 为什么要克隆? 如何实现克隆 浅克 ...

  9. (转)Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

    原文地址:http://blog.csdn.net/kenthong/article/details/5758884 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的 ...

随机推荐

  1. delphi中 panel如何在Form实现鼠标移动拖放

    Panel的MouseDown事件 移动就写上ReleaseCapture;SendMessage(Panel1.Handle,wm_SysCommand,$F012,0); 改变大小就写上Relea ...

  2. sha1withRSA算法

    RAS_USE_PRIVATE_ENCRYPT(3021300906052b0e03021a05000414 + SHA1(DATA))

  3. Asp.net Zero 应用实战-官方示例PhoneBook学习1

    适用Zero版本:ASP.NET Core & Angular 2+ (aspnet-zero-core-3.1.0). 该版本官方有两个solution文件夹:Angular(前端) 和 a ...

  4. MongoDB学习记录(三) - MongoDB的"增查改删"操作之"查"

    查找使用的方法: db.collection.find() 查找所有文档 db.collection.find({})或者db.collection.find({}) 指定键值对 db.collect ...

  5. list.get(0)防止NPE

    package com.chuangjin.p2p; import com.chuangjin.core.utils.JsonUtils; import java.util.ArrayList; im ...

  6. Effective C++ 笔记:条款 34 实现继承和接口继承

    Differentiate between inheritance of interface and inheritance of implementation. 行为含义 声明一个pure virt ...

  7. Django之发送邮件

    Django的发送邮件是基于django的一个组件进行操作的,EmailMessage 基本使用方法: def send_html_mail(subject, html_content, from_a ...

  8. spring boot + vue + element-ui全栈开发入门

    今天想弄弄element-ui  然后就在网上找了个例子 感觉还是可以用的  第一步是完成了  果断 拿过来  放到我这里这  下面直接是连接  点进去 就可以用啊 本想着不用vue   直接导入连接 ...

  9. #pragma常用预处理指令

    #pragma pack(1):1字节对齐#pragma once:指定头文件被编译一次#pragma message("message"):编译时输出message文本#prag ...

  10. metasploit生成payload的格式

    转自https://www.cnblogs.com/zlgxzswjy/p/6881904.html Often one of the most useful (and to the beginner ...