5. 深克隆还有哪些实现方式?
深克隆的实现方式很多,总的来说有以下几种:
- 所有对象都实现克隆方法。
- 通过构造方法实现深克隆。
- 使用 JDK 自带的字节流。
- 使用第三方工具实现,比如:Apache Commons Lang。
- 使用 JSON 工具类实现,比如:Gson,FastJSON 等等。
1、有对象都实现克隆方法
/** * Project Name:review_java <br/> * Package Name:com.nasus.clone <br/> * Date:2021/1/31 20:57 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class DeepCloneOneExample { static class User implements Cloneable { public User() { } public User(Integer age, String name, Address address) { this.age = age; this.name = name; this.address = address; } // 年龄 private Integer age; // 名称 private String name; // 地址 private Address address; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @Override protected Object clone() throws CloneNotSupportedException { User user = (User) super.clone(); // 引用类型克隆赋值 user.setAddress((Address) this.address.clone()); return user; } } static class Address implements Cloneable { public Address() { } public Address(String province, String city) { this.province = province; this.city = city; } // 省份 private String province; // 城市 private String city; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override protected Object clone() throws CloneNotSupportedException { return (Address) super.clone(); } } public static void main(String[] args) throws CloneNotSupportedException { // 创建被赋值对象 Address address = new Address("广东", "广州"); User userOne = new User(22, "clone", address); // 克隆 userOne 对象 User userTwo = (User) userOne.clone(); // 修改原型对象 userOne.getAddress().setCity("清远"); // 输出 p1 和 p2 地址信息 System.out.println("userOne:" + userOne.getAddress().getCity() + ",userTwo:" + userTwo.getAddress().getCity()); } }
运行结果:
userOne:清远,userTwo:广州
可以看到,修改了原型对象的引用对象并没有改变克隆对象的引用对象。说明两者引用对象已经不是同一个引用对象了,所以是深克隆。
2、通过构造方法实现深克隆
《Effective Java》 中「推荐使用构造器(Copy Constructor)来实现深克隆,如果构造器的参数为基本数据类型或字符串类型则直接赋值,如果是对象类型,则需要重新 new 一个对象」,实现代码如下:
/** * Project Name:review_java <br/> * Package Name:com.nasus.clone <br/> * Date:2021/1/31 21:16 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class DeepCloneSecondExample { static class User { public User() { } public User(Integer age, String name, Address address) { this.age = age; this.name = name; this.address = address; } // 年龄 private Integer age; // 名称 private String name; // 地址 private Address address; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } static class Address { public Address() { } public Address(String province, String city) { this.province = province; this.city = city; } // 省份 private String province; // 城市 private String city; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } public static void main(String[] args) throws CloneNotSupportedException { // 创建对象 Address address = new Address("广东", "广州"); User userOne = new User(22, "clone", address); // 调用构造函数克隆对象 User userTwo = new User(userOne.getAge(), userOne.getName(), new Address(userOne.getAddress().getProvince(), userOne.getAddress().getCity())); // 修改原型对象 userOne.getAddress().setCity("清远"); // 输出 userOne 和 userOne 地址信息 System.out.println("userOne:" + userOne.getAddress().getCity() + ",userTwo:" + userTwo.getAddress().getCity()); } }
运行结果:
userOne:清远,userTwo:广州
3、使用 JDK 自带的字节流
使用 JDK 自带字节流实现,先将要原型对象写入到内存中的字节流,再从这个字节流中读出刚刚存储的信息,作为一个新对象返回,此时这个新对象和原型对象就不存在任何地址上的共享,从而实现深克隆,代码如下:
/** * Project Name:review_java <br/> * Package Name:com.nasus.clone <br/> * Date:2021/1/31 21:25 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class DeepCloneThirdExample { static class User implements Serializable{ public User() { } public User(Integer age, String name, Address address) { this.age = age; this.name = name; this.address = address; } // 年龄 private Integer age; // 名称 private String name; // 地址 private Address address; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } static class Address implements Serializable { public Address() { } public Address(String province, String city) { this.province = province; this.city = city; } // 省份 private String province; // 城市 private String city; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } /** * 通过字节流实现克隆 */ static class StreamClone { public static <T extends Serializable> T clone(User user) { T cloneObj = null; try { // 写入字节流 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bo); oos.writeObject(user); oos.close(); // 分配内存,写入原始对象,生成新对象 // 获取上面的输出字节流 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); // 返回生成的新对象 cloneObj = (T) oi.readObject(); oi.close(); } catch (Exception e) { e.printStackTrace(); } return cloneObj; } } public static void main(String[] args) throws CloneNotSupportedException { // 创建对象 Address address = new Address("广东", "广州"); User userOne = new User(22, "clone", address); // 调用构造函数克隆对象 User userTwo = StreamClone.clone(userOne); // 修改原型对象 userOne.getAddress().setCity("清远"); // 输出 userOne 和 userOne 地址信息 System.out.println("userOne:" + userOne.getAddress().getCity() + ",userTwo:" + userTwo.getAddress().getCity()); } }
运行结果:
userOne:清远,userTwo:广州
结果还是一样的,但这里要注意下。由于是通过字节流序列化实现的深克隆,所以每个对象必须能被序列化。也即必须实现 Serializable 接口。
4、通过第三方工具实现深克隆
比如:Apache Commons Lang,实现代码如下:
/** * Project Name:review_java <br/> * Package Name:com.nasus.clone <br/> * Date:2021/1/31 21:41 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class DeepCloneFourthExample { static class User implements Serializable { public User() { } public User(Integer age, String name, Address address) { this.age = age; this.name = name; this.address = address; } // 年龄 private Integer age; // 名称 private String name; // 地址 private Address address; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } static class Address implements Serializable { public Address() { } public Address(String province, String city) { this.province = province; this.city = city; } // 省份 private String province; // 城市 private String city; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } public static void main(String[] args) { // 创建对象 Address address = new Address("广东", "广州"); User userOne = new User(22, "clone", address); // 调用 apache.commons.lang 克隆对象 User userTwo = SerializationUtils.clone(userOne); // 修改原型对象 userOne.getAddress().setCity("清远"); // 输出 userOne 和 userOne 地址信息 System.out.println("userOne:" + userOne.getAddress().getCity() + ",userTwo:" + userTwo.getAddress().getCity()); } }
输出结果:
userOne:清远,userTwo:广州
实际上,这种工作中我自己是用的比较多的一种方法。因为方便,它跟第三种有点像。其实底层是一样的,还是李用字节流实现。
5、使用 JSON 工具类实现
比如 Gson 或者 FastJson 等等,下面以 Gson 为例,实现代码如下:
/** * Project Name:review_java <br/> * Package Name:com.nasus.clone <br/> * Date:2021/1/31 21:58 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class DeepCloneFifthExample { static class User { public User() { } public User(Integer age, String name, Address address) { this.age = age; this.name = name; this.address = address; } // 年龄 private Integer age; // 名称 private String name; // 地址 private Address address; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } static class Address { public Address() { } public Address(String province, String city) { this.province = province; this.city = city; } // 省份 private String province; // 城市 private String city; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } public static void main(String[] args) { // 创建对象 Address address = new Address("广东", "广州"); User userOne = new User(22, "clone", address); // 调用 Gson 克隆对象 Gson gson = new Gson(); User userTwo = gson.fromJson(gson.toJson(userOne), User.class); // 修改原型对象 userOne.getAddress().setCity("清远"); // 输出 userOne 和 userTwo 地址信息 System.out.println("userOne:" + userOne.getAddress().getCity() + ",userTwo:" + userTwo.getAddress().getCity()); } }
运行结果:
userOne:清远,userTwo:广州
这种方法会先把对象转化成字符串,再从字符串转化成新的对象,因为新对象是从字符串转化而来的,因此不会和原型对象有任何的关联,所以实现了深克隆。
6. 总结
本文介绍了深浅克隆的概念、区别;怎么实现客隆;克隆有啥约定俗成的规则;
Arrays.copy () 是深克隆还是浅克隆;以及深刻龙的几种实现方式。希望对你有帮助~