1. 克隆的用处

在日常编码中我们经常需要产生某个对象的副本,这里的副本并不是指向同一个对象的不同引用,而是与当前对象状态一模一样的另一个新的对象。如果使用单纯的引用赋值,会发生什么效果呢?

我们可以观察下面的代码:

package com.coderap.foundation.clone;

class Address {

public String province;

public String city;

public Address(String province, String city) {

this.province = province;

this.city = city;

}

@Override

public String toString() {

return "Address{" +

"province='" + province + '\'' +

", city='" + city + '\'' +

'}';

}

}

class Person {

public String name;

public Integer age;

public Address address;

public Person(String name, Integer age) {

this.name = name;

this.age = age;

}

}

public class CloneTest {

public static void main(String[] args) {

Person person = new Person("Tom", 20);

person.address = new Address("CA", "Los Angeles");

System.out.println("before: " + person.name);

System.out.println("before: " + person.age);

System.out.println("before: " + person.address);

Person newPerson = (Person) person;

person.name = "Jack";

person.age = 22;

newPerson.address.province = "CA";

newPerson.address.city = "Nevada";

System.out.println("after: " + person.name);

System.out.println("after: " + person.age);

System.out.println("after: " + person.address);

System.out.println("person equal newPerson ? " + person.equals(newPerson));

}

}

在上面的代码中我们单纯地将一个新的引用指向一个已有的对象,然后使用新的引用对对象进行操作,可以发现,所有的更改在两个引用上都体现出来了:

before: Tom

before: 20

before: Address{province='CA', city='Los Angeles'}

after: Jack

after: 22

after: Address{province='CA', city='Nevada'}

person equal newPerson ? true

在Java中,两个引用同时指向相同的对象时,这两个引用是指向的同一块内存,所以使用任何一个引用对内存的操作都将直接反映到另一个引用上,单纯的引用赋值是不能够克隆对象的。为了解决克隆问题,Java提供了Cloneable接口和clone()方法。

2. Cloneable 接口和 clone 方法

Cloneable接口是一个标记接口,其中没有任何内容,定义如下:

package java.lang;

public interface Cloneable {}

clone()方法是在Object类中定义的:

protected native Object clone() throws CloneNotSupportedException;

clone()方法是被protected修饰的受保护的方法,类只有实现了Cloneable接口,才可以在该类的实例上调用clone()方法,否则会抛出CloneNotSupportException异常。

Object的clone()方法创建并返回此对象的一个副本。对于任何对象o,clone()方法有以下的规则:

o.clone() != o为true;

o.clone().getClass() == o.getClass()为true;

o.clone().equals(o)一般情况下为true,但这并不是必须要满足的要求。

Object中默认的实现是一个浅克隆,但是该方法是有缺陷的,如果需要实现深层次克隆的话,必须对类中可变域生成新的实例。

2.1. 浅克隆

浅克隆并不会把对象所有属性全部克隆一份,而是有选择性的克隆,克隆规则如下:

基本类型。如果变量是基本类型,则克隆其值,比如int、float、long等。

String字符串,对于字符串的克隆比较特殊,克隆的是引用地址,但是在修改的时候,它会从字符串池(String Pool)中重新生成新的字符串,原有的字符串对象保持不变,此处可以认为String是个基本类型。

对象。如果变量时一个实例对象,则克隆地址引用,也就是说此时新克隆出的对象与原有对象共享该实例变量,不受访问权限的限制。这中克隆操作是非常危险的,意味着不同的对象之间对某些引用对象是共有的,相互修改将受到影响。

注1:基本数据类型在克隆时是进行的原值克隆。

如下面的代码,我们只是简单的在Person类中实现了Cloneable接口并且重写了clone()方法,同时进行克隆操作:

package com.coderap.foundation.clone;

class Address {

public String province;

public String city;

public Address(String province, String city) {

this.province = province;

this.city = city;

}

@Override

public String toString() {

return "Address{" +

"province='" + province + '\'' +

", city='" + city + '\'' +

'}';

}

}

class Person implements Cloneable {

public String name;

public Integer age;

public Address address;

public Person() {

System.out.println("Person() execute");

}

public Person(String name, Integer age) {

System.out.println("Person(String name, Integer age) execute");

this.name = name;

this.age = age;

}

@Override

protected Object clone() throws CloneNotSupportedException {

return super.clone();

}

}

public class CloneTest {

public static void main(String[] args) throws CloneNotSupportedException {

Person person = new Person("Tom", 20);

person.address = new Address("CA", "Los Angeles");

System.out.println("before: " + person.name);

System.out.println("before: " + person.age);

System.out.println("before: " + person.address);

Person newPerson = (Person)person.clone();

newPerson.name = "Jack";

newPerson.age = 22;

newPerson.address.province = "CA";

newPerson.address.city = "Nevada";

System.out.println("after: " + person.name);

System.out.println("after: " + person.age);

System.out.println("after: " + person.address);

System.out.println("person != newPerson ? " + String.valueOf(person != newPerson));

System.out.println("person getClass equal newPerson getClass ? " + person.getClass().equals(newPerson.getClass()));

System.out.println("person equal newPerson ? " + person.equals(newPerson));

}

}

运行上面的代码,可以得到打印信息如下:

Person(String name, Integer age) execute

before: Tom

before: 20

before: Address{province='CA', city='Los Angeles'}

after: Tom

after: 20

after: Address{province='CA', city='Nevada'}

person != newPerson ? true

person getClass equal newPerson getClass ? true

person equal newPerson ? false

我们可以得出以下结果:

克隆一个对象不会重复调用对应类的构造方法;

上述最后的三条的判断的结果是遵循了clone()方法三条规则的;

基本类型和String类型的数据都是独立的,并不会收到新对象的影响,但是引用类型的对象会受到新对象的影响。

需要注意的是,在修改城市信息时,如果我们直接指定newPerson.address = new Address("CA", "Nevada")其实是不会影响到原来的person对象的,因为虽然newPerson和person的address指向的同一个Address对象,但使用newPerson.address = new Address("CA", "Nevada")会给newPerson对象生成一个新的Address对象,并将newPerson的address引用指向这个新的对象,所以并不会影响到原有的person对象的address对象属性。

Java中实现了Cloneable接口的类有很多,如ArrayList、Calendar、Date、HashMap、Hashtable、HashSet、LinkedList等等。我们在使用这些类时并不需要考虑浅克隆带来的影响。

2.2. 深克隆

深克隆操作应该将除自身对象以外的所有对象,包括自身所包含的所有对象实例都进行克隆。

其实Object的clone()方法提供的是一种浅克隆的机制,如果想要实现对对象的深克隆,有两种办法:

先对对象进行序列化,紧接着马上反序列化出;

先调用super.clone()方法克隆出一个新对象来,然后在子类的clone()方法中手动给克隆出来的非基本数据类型(引用类型)赋值,比如ArrayList的clone()方法:

/**

* Returns a shallow copy of this ArrayList instance. (The

* elements themselves are not copied.)

*

* @return a clone of this ArrayList instance

*/

public Object clone() {

try {

ArrayList> v = (ArrayList>) super.clone();

v.elementData = Arrays.copyOf(elementData, size);

v.modCount = 0;

return v;

} catch (CloneNotSupportedException e) {

// this shouldn't happen, since we are Cloneable

throw new InternalError(e);

}

}

java的cloneable_Java的Cloneable接口和clone方法相关推荐

  1. java cloneable_java开发——Cloneable接口、clone()方法和深浅拷贝

    1.实现Cloneable接口表明该类的对象是允许克隆的. 2.允许克隆的意思是:可以调用clone()方法. 3.深拷贝还是浅拷贝,取决于如何重写Object的clone()方法. 4.原对象和克隆 ...

  2. Java基础篇:对象拷贝:clone方法 以及 序列化

    我们知道在Java中存在这个接口Cloneable,实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面比我们直接通过new生成对象来的快,特别是在大对象的生成上,使得性能的提升非常 ...

  3. 十三、Java高级特性 Lambda表达式 | 接口组成更新 | 方法引用 | 函数式接口

    文章目录 十三.Java高级特性 1.Lambda表达式 1.1体验Lambda表达式[理解] 1.2Lambda表达式的标准格式[理解] 1.3Lambda表达式练习1[应用] 1.4Lambda表 ...

  4. java 克隆对象_Java 中如何使用clone()方法克隆对象?

    java为什么要 对象克隆: 在程序开发时,有时可能会遇到以下情况:已经存在一个对象A,现在需要一个与A对象完全相同的B 对象,并对B 对象的属性值进行修改,但是A 对象原有的属性值不能改变.这时,如 ...

  5. JAVA集合1(Collection接口,iterator()方法,增强型for循环)

    JAVA集合框架概述 集合框架涉及到的api List接口是继承Collection接口,Set接口是继承Collection接口, ArrayList 类是一个可以动态修改的数组,与普通数组的区别就 ...

  6. Mybatis莫名报错或Mapper.xml配置后爆红或显示The error may exist in com/jdsydwr/dao/UserMapper.java找不到Mapper接口的修改方法

    报错内容 org.apache.ibatis.exceptions.PersistenceException:  ### Error building SqlSession. ### The erro ...

  7. JAVA小练习102——List接口的特有方法(增删改)

    import java.util.ArrayList; import java.util.List;public class Demo102 {public static void main(Stri ...

  8. java定义一个接口shape_编写一个java应用程序,定义一个接口,包含一个方法areas(),在rectangle勒,squ...

    import static java.lang.Math.*; import java.util.*; interface Shape{ void areas(); } class rectangle ...

  9. (二十三)原型模式详解(clone方法源码的简单剖析)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 原型模式算是JAVA中最简单 ...

最新文章

  1. JVM基础面试题及原理讲解
  2. PHP命名规范【转】
  3. 国际农产品市场谋定特点趋势 对话国际农民丰收节贸易会
  4. ruby 安装和管理
  5. php内存映射,如何用ZwMapViewOfSection将Driver分配的内存映射到App空间?
  6. linux克隆出现mac地址错误
  7. 二项分布 , 多项分布, 以及与之对应的beta分布和狄利克雷分布
  8. 雷霄骅--H264视频编解码分析--目录转载
  9. 河海大学计算机科学考研真题,河海大学计算机与信息学院845电子技术基础历年考研真题汇编.pdf...
  10. 自定义服务器网址,小白新手如何在服务器上搭建一个自己的网站
  11. spark学习-74-源代码:Endpoint模型介绍(6)-Endpoint的消息的接收
  12. java上下左中右的布局面板_JAVA GUI重要知识点学习心得
  13. jxl freemark
  14. (Kinetis K60) FTM输出PWM
  15. 什么是OA系统?OA系统是什么?OA是什么意思?
  16. EF 通用帮助类 含分页 Lambda 拼接表达式
  17. I.MX6Q(TQIMX6Q/TQE9)学习笔记——新版BSP之kernel移植
  18. python包装好后在pycharm IED中无法导入
  19. 宝塔docker安装Halo
  20. 关于C++中公有继承、私有继承、保护继承的讨论

热门文章

  1. android真机调试 红米,红米Note3 USB调试在哪里?红米Note3打开usb调试模式方法图解...
  2. MySQL——基础篇
  3. velve - Left 4 Dead AI
  4. 国产数据库清单;微盟《生产环境和数据恢复》;TiDB招聘;Oracle备份还原指南、GaussDB性能调优指南……墨天轮数据库周刊-第5期
  5. c语言double字母,C语言double
  6. 【网络篇】第十四篇——HTTP协议(一)(附带电视剧李浔同款爱心+端口号被恶意占用如何清除)
  7. Ubuntu 20.04 安装 QuestaSim 2021踩坑记录
  8. 实习日记——Day3
  9. H3C交换机端口镜像
  10. 计算机科学与技术学院举办活动,计算机科学与技术学院举办重阳节活动