Java-深克隆&浅克隆
2021/7/5 14:37:50
本文主要是介绍Java-深克隆&浅克隆,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
浅克隆(Shadow Clone)
是把原型对象中成员变量为值类型的属性都复制给克隆对象,把原型对象中成员变量为引用类型的引用地址也复制给克隆对象,也就是原型对象中如果有成员变量为引用对象,则此引用对象的地址是共享给原型对象和克隆对象的。
简单来说就是浅克隆只会复制原型对象,但不会复制它所引用的对象,如下图所示:
深克隆(Deep Clone)
是将原型对象中的所有类型,无论是值类型还是引用类型,都复制一份给克隆对象,也就是说深克隆会把原型对象和原型对象所引用的对象,都复制一份给克隆对象,如下图所示:
Java 实现克隆
需要实现 Cloneable 接口,并重写 Object 类中的 clone() 方法,实现代码如下:
/** * author:wy * describe:Java 实现克隆 */ @Data @NoArgsConstructor @AllArgsConstructor public class People implements Cloneable { public static void main(String[] args) { People p1 = new People(); p1.setId(1); p1.setName("Java"); People p2 = null; try { // 克隆p1对象 p2 = (People) p1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } System.out.println(p1);// People(id=1, name=Java) System.out.println(p2);// People(id=1, name=Java) System.out.println(p1 == p2);// false System.out.println(p1.getClass() == p2.getClass());// true System.out.println(p1.equals(p2));// true p1.setName("Android"); System.out.println(p1);// People(id=1, name=Android) System.out.println(p2);// People(id=1, name=Java) } // 属性 private Integer id; private String name; /** * 重写 Object 类中的 clone() 方法 * @return * @throws CloneNotSupportedException */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
知识扩展
1. clone()
深克隆
// 是使用 native 修饰的本地方法,因此执行的性能会很高 protected native Object clone() throws CloneNotSupportedException;
从以上源码的注释信息中我们可以看出,Object 对 clone() 方法的约定有三条:
- 对于所有对象来说,x.clone() !=x 应当返回 true,因为克隆对象与原对象不是同一个对象;
- 对于所有对象来说,x.clone().getClass() == x.getClass() 应当返回 true,因为克隆对象与原对象的类型是一样的;
- Ba对于所有对象来说,x.clone().equals(x) 应当返回 true,因为使用 equals 比较时,它们的值都是相同的。
2. Arrays.copyOf()
浅克隆
@Test public void test() { People[] o1 = {new People(1, "Java")}; People[] o2 = Arrays.copyOf(o1, o1.length); // 修改原型对象的第一个元素的值 o1[0].setName("Android"); System.out.println(o1[0]);// People(id=1, name=Android) System.out.println(o2[0]);// People(id=1, name=Android) }
因为数组比较特殊,数组本身就是引用类型,因此在使用
Arrays.copyOf()
其实只是把引用地址复制了一份给克隆对象,如果修改了它的引用对象,那么指向它的(引用地址)所有对象都会发生改变,因此看到的结果是,修改了克隆对象的第一个元素,原型对象也跟着被修改了。
3. 深克隆实现方式汇总
深克隆的实现方式有很多种,大体可以分为以下几类:
- 所有对象都实现克隆方法;
- 通过构造方法实现深克隆;
- 使用 JDK 自带的字节流实现深克隆;
- 使用第三方工具实现深克隆,比如 Apache Commons Lang;
- 使用 JSON 工具类实现深克隆,比如 Gson、FastJSON 等。
1. 所有对象都实现克隆
/** * author:wy * describe:收货人 */ @Data @NoArgsConstructor @AllArgsConstructor public class Consignee implements Cloneable { public static void main(String[] args) throws CloneNotSupportedException { Address address = new Address(101, "上海"); Consignee c1 = new Consignee(1, "骑士", address); // 克隆 c1 对象 Consignee c2 = c1.clone(); // 修改原型对象 c1.getAddress().setCity("北京"); System.out.println(c1);// Consignee(id=1, name=骑士, address=Address(id=101, city=北京)) System.out.println(c2);// Consignee(id=1, name=骑士, address=Address(id=101, city=上海)) } private Integer id; private String name; private Address address;// 包含 Address 对象 @Override protected Consignee clone() throws CloneNotSupportedException { Consignee consignee = (Consignee) super.clone(); consignee.setAddress(this.address.clone());// 引用类型克隆赋值 return consignee; } }
/** * author:wy * describe:地址 */ @Data @NoArgsConstructor @AllArgsConstructor public class Address implements Cloneable { private Integer id; private String city; @Override protected Address clone() throws CloneNotSupportedException { return (Address) super.clone(); } }
从结果可以看出,当我们修改了原型对象的引用属性之后,并没有影响克隆对象,这说明此对象已经实现了深克隆。
2. 通过构造方法实现深克隆
《Effective Java》 中推荐使用构造器(Copy Constructor)来实现深克隆,如果构造器的参数为基本数据类型或字符串类型则直接赋值,如果是对象类型,则需要重新 new 一个对象,实现代码如下:
/** * author:wy * describe:收货人2 */ @Data @NoArgsConstructor @AllArgsConstructor public class Consignee2 { public static void main(String[] args) { Address2 address = new Address2(102, "北京"); Consignee2 c1 = new Consignee2(2, "骑士梦", address); // 调用构造函数克隆对象 Consignee2 c2 = new Consignee2(c1.getId(), c1.getName(), new Address2(c1.getAddress().getId(), c1.getAddress().getCity())); // 修改原型对象 c1.getAddress().setCity("上海"); System.out.println(c1);// Consignee2(id=2, name=骑士梦, address=Address2(id=102, city=上海)) System.out.println(c2);// Consignee2(id=2, name=骑士梦, address=Address2(id=102, city=北京)) } private Integer id; private String name; private Address2 address;// 包含 Address 对象 }
/** * author:wy * describe:地址2 */ @Data @NoArgsConstructor @AllArgsConstructor public class Address2 { private Integer id; private String city; }
从结果可以看出,当我们修改了原型对象的引用属性之后,并没有影响克隆对象,这说明此对象已经实现了深克隆。
3. 通过字节流实现深克隆
这篇关于Java-深克隆&浅克隆的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27消息中间件底层原理资料详解
- 2024-11-27RocketMQ底层原理资料详解:新手入门教程
- 2024-11-27MQ底层原理资料详解:新手入门教程
- 2024-11-27MQ项目开发资料入门教程
- 2024-11-27RocketMQ源码资料详解:新手入门教程
- 2024-11-27本地多文件上传简易教程
- 2024-11-26消息中间件源码剖析教程
- 2024-11-26JAVA语音识别项目资料的收集与应用
- 2024-11-26Java语音识别项目资料:入门级教程与实战指南
- 2024-11-26SpringAI:Java 开发的智能新利器