Java实现对象克隆的方法

1、Java实现克隆有两种形式

  • 浅拷贝
  • 深拷贝

在Java中吗,我们说两个对象是否相等通常有两层含义:

  • 对象的内容是否相等,通常使用到对象的 equals(Object o) 函数;
  • 引用的地址是否相同,使用运算符 == 比较即可。

当两个对象通过赋值符号 = 赋值时,表明这两个对象指向了内存中同一个地址,所以改变其中一个对象的内容,也就间接地改变了另一个对象的内容。有时候,我们需要从一个已经存在的对象重新拷贝一份出来,并且不仅这两个对象内容相等,在内存中存在两个独立的存储地址,互不影响,这时,就需要用到 Java 中的克隆机制。

2、Cloneable

通过 Cloneable 接口可以很轻松地实现 Java 对象的克隆,只需要 implements Cloneable 并实现 Object 的 clone() 方法即可。


public class User implements Cloneable{private String username;private String password;public User(String username, String password) {super();this.username = username;this.password = password;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic boolean equals(Object obj) {User user = (User) obj;if (username.equals(user.username) && password.equals(user.password)) {return true;}return false;}}

注意这里对象实现的是 Object 类的 clone() 方法,因为 Cloneable 是一个空接口:

package java.lang;public interface Cloneable {}

从源码注释中可以看出,需要实现 Object 类中的 clone() 方法(注意:clone() 函数是一个 native 方法,同时抛出了一个异常)

protected native Object clone() throws CloneNotSupportedException;

从 clone() 函数的注释中能够看出对象与克隆对象之间的关系,测试代码如下(注意:我们在 User 对象中重写了 equals() 函数)

    public static void main(String[] args) throws CloneNotSupportedException{User userOne, userTwo, userThree;userOne = new User("username", "password");userTwo = userOne;userThree = (User) userOne.clone();System.out.println(userTwo==userOne);            //trueSystem.out.println(userTwo.equals(userOne));    //trueSystem.out.println(userThree==userOne);            //falseSystem.out.println(userThree.equals(userOne));    //true}

测试结果显示,通过 clone() 函数,我们成功地从 userOne 对象中克隆出了一份独立的 userThree 对象。

3、浅拷贝和深拷贝的区别

2.1 浅拷贝

1、实现Cloneable接口,重写Object中的clone()方法
谈此之前,我们先看一个例子,定义一个名为 Company 的类,并添加一个类型为 User 的成员变量:


public class Company implements Cloneable{private User user;private String address;public Company(User user, String address) {super();this.user = user;this.address = address;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic boolean equals(Object obj) {Company company = (Company) obj;if (user.equals(company.getUser()) && address.equals(company.address)) {return true;}return false;}}

测试代码及测试结果如下:

    public static void main(String[] args) throws CloneNotSupportedException{Company companyOne, companyTwo, companyThree;companyOne = new Company(new User("username", "password"), "上海市");companyTwo = companyOne;companyThree = (Company) companyOne.clone();System.out.println(companyTwo==companyOne);                //trueSystem.out.println(companyTwo.equals(companyOne));        //trueSystem.out.println(companyThree==companyOne);            //falseSystem.out.println(companyThree.equals(companyOne));    //trueSystem.out.println(companyThree.getUser()==companyOne.getUser());            //true ? 这里为什么不是false呢System.out.println(companyThree.getUser().equals(companyOne.getUser()));    //true}

问题来了,companyThree 与 companyOne 中的 User 是同一个对象!也就是说 companyThree 只是克隆了 companyOne 的基本数据类型的数据,而对于引用类型的数据没有进行深度的克隆。也就是俗称的浅克隆。

浅克隆:顾名思义,就是很表层的克隆,只克隆对象自身的引用地址;

深克隆:也称“N层克隆”,克隆对象自身以及对象所包含的引用类型对象的引用地址。

这里需要注意的是,对于基本数据类型(primitive)和使用常量池方式创建的String 类型,都会针对原值克隆,所以不存在引用地址一说。当然不包括他们对应的包装类。

2.2 深拷贝

1、递归调用clone()方法

所以使用深克隆就可以解决上述 Company 对象克隆过后两个 user 对象的引用地址相同的问题。我们修改一下 Company 类的 clone() 函数。

    @Overrideprotected Object clone() throws CloneNotSupportedException {Company company = (Company) super.clone();company.user = (User) company.getUser().clone();return company;}

再运行测试代码,就能得到 companyThree.getUser()==companyOne.getUser() 为 false 的结果了。从实现过程来说,递归克隆存在克隆过程多且复杂的缺点

2、通过序列化方式

public class Text implements Serializable{private static final long serialVersionUID = 8723901148964L;private int age;private Name name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Name getName() {return name;}public void setName(Name name) {this.name = name;}public Object myClone(){Text text=null;ByteArrayOutputStream bos=new ByteArrayOutputStream();try {ObjectOutputStream oos=new ObjectOutputStream(bos);oos.writeObject(this);ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois=new ObjectInputStream(bis);text=(Text)ois.readObject();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return text;}
}class Name implements Serializable {private static final long serialVersionUID = 872390113109L;private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return name;}
}

结果分析:

采用深克隆能有效隔离源对象与克隆对象的联系。

从实现过程来说,递归克隆存在克隆过程多且复杂的缺点,所以建议采用序列化的方式进行深克隆。

总结

Java对象克隆共有两种形式,三种方法

  • 浅拷贝

    • 调用clone方法
  • 深拷贝

    • 递归调用clone方法

    • 序列化对象

三种方法之间互有优缺点,具体采用要根据实际情况。

Java实现对象的克隆方式相关推荐

  1. JAVA之对象的克隆

    JAVA之对象的克隆 1.为什么要克隆? ①方便,克隆的对象可能包含一些已经修改过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的"状态" ...

  2. java 复制对象有哪些方式

    2019独角兽企业重金招聘Python工程师标准>>> java 复制对象有哪些方式 Apache的 Common beanutils库 org.apache.commons.bea ...

  3. JAVA复习(对象的克隆、正则表达式)

    对象的克隆 对象的克隆就是复制的操作,和之前学习的引用传递不同 在引用传递中一个 实例 可以有多个名字 但是多个名字都指向一个对象 克隆出来的对象 和之前的对象 没有任何的引用关系 想要实现对象的克隆 ...

  4. Java实现对象深度克隆

    两种方式:   1). 实现Cloneable接口并重写Object类中的clone()方法:   2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆 ...

  5. java script 对象

    java script 对象 1.创建方式 1)通过字面量的形式创建 例:var = stt{x:1,y:2,y:3}; 或:var = stt{ x:1, y:2, 'for':3 } 注意关键字必 ...

  6. Java中对象的深复制(深克隆)和浅复制(浅克隆)介绍

    1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵ ...

  7. Java提高—对象克隆(复制)/对象属性拷贝

    对象克隆(复制)假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byt ...

  8. java对象实例化的方式

    java对象实例化的方式有以下几种: 1.使用new 2.工厂模式 3.反射 4.clone()方法 5.反序列化方式 /** 实现Cloneable和Serializable接口 */ public ...

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

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

最新文章

  1. saltstack常用命令
  2. 开发板与linux文件系统,基于topeer 4412开发板 ***面linux文件系统的制作
  3. Tensorflow2.0报错:ProfilerNotRunningError: Cannot stop profiling. No profiler is running.
  4. HDU5794 - A Simple Chess
  5. win7 mac虚拟机linux,Mac虚拟机parallels desktop超详细安装Win7图文分解
  6. android plot,androidplot行不显示
  7. linux缓冲设备,Linux设备驱动程序缓冲策略
  8. 思科CISCO常用命令汇总
  9. 微信群如何实现语音多群转播
  10. Solaris系统root用户无法直接登录
  11. pos 指令集 linux,Linux系统下10大开源POS系统
  12. 厚着脸皮求领导写了一篇java小白进阶大牛之路!!!
  13. Android学生信息管理系统
  14. 从 Chrome 源码看浏览器如何计算 CSS
  15. 肿瘤免疫疗法 | 细胞治疗和PD1/PDL1 | Tumor immunotherapy | cell therapy
  16. 联想拯救者R720双系统如何进bios
  17. 新冠“登陆”南极,中国极地科考“零感染”
  18. Java项目:(小程序)前台+后台相结合在线点餐系统(spring+spring mvc+mybatis+layui+微信小程)
  19. 流氓软件终极杀手 Universal Extractor 【 推荐一个 流氓软件客星 】
  20. torch.mul() 和 torch.mm() 的区别

热门文章

  1. java常量、变量的定义和使用
  2. http.js 总结
  3. 系统hosts文件配置
  4. android 游戏移植 (一) (文末有福利) | SDL 西游释厄传调试
  5. 如何打开.jar文件
  6. 左程云大厂算法刷题班——05
  7. python樱花_武汉大学樱花绽放之Python版
  8. 优化电脑 加快电脑的速度。 垃圾处理器,加快网速
  9. 计算机网络状态不存在,网络不存在或尚未启动\以及局域网互访的解决办法
  10. 如何查看Android手机CPU类型是armeabi,armeabi-v7a,还是arm64-v8a?