浅克隆:
实现Cloneable接口即可实现,浅克隆只对象内部的基础数据类型(包括包装类)被克隆,引用数据类型(负责对象)会被使用引用的方式传递。
简单来说,就是浅克隆属性如果是复杂对象,对象是不会被复制一份的。
示例如下:
package com.comleader.utils.test;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: Daisen.Z
* @Date: 2023/3/10 9:53
* @Version: 1.0
* @Description:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Friend {
private String name;
private Integer age;
private String home;
}
注意: 需要重新clone方法,将方法的protected改为public
package com.comleader.utils.test;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: Daisen.Z
* @Date: 2023/3/10 9:53
* @Version: 1.0
* @Description:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Cloneable {
private String name;
private Integer age;
private String home;
private Friend friend;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Person person_old = new Person();
person_old.setName("张三");
person_old.setAge(18);
person_old.setHome("chengdu");
person_old.setFriend(new Friend("李四",19,"chongqin"));
System.out.println("初始化下的person:" + person_old);
Person person_clone = (Person) person_old.clone();
person_clone.setName("张武");
person_clone.setAge(19);
person_clone.getFriend().setAge(20);
System.out.println("克隆出来的person:" + person_clone);
System.out.println("修改之后的person:" + person_clone);
System.out.println("修改后的原person:" + person_old);
}
}
运行Person中的main方法,
观察结果可以看出,修改Person的克隆对象的friend复杂属性时,原对象包括原对象的所有克隆对象friend熟悉都会被修改。
而修改String和Integer的则互不影响。
补充知识:
clonable接口的克隆方式,相比较于new对象的方式,不需要再计算内存分布,而是直接将内存区域分配给克隆对象,在大数据量的重复对象创建时可以使用clonable来优化性能。
深克隆:
深克隆的方式只能自己重新Cloneable方法或者序列化的方式实现。
重新克隆方法的方式:
1、 Friend实现Cloneable接口,并重写clone()方法。
@Data
@NoArgsConstructor
@AllArgsConstructor
class Friend implements Cloneable{
private String name;
private Integer age;
private String home;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2、 更改Person中的clone()方法
@Data
@NoArgsConstructor
@AllArgsConstructor
class Person implements Cloneable{
private String name;
private Integer age;
private String home;
private Friend friend;
@Override
public Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.friend = (Friend) friend.clone();
return person;
}
}
这样克隆时,内部复杂对象也被克隆。
通过序列化对象的方式
如果当类中的属性存在数组(数组不能实现Cloneable接口)或者属性之间的关系比较复杂时,上面的方法都不能很好的实现深克隆了。
序列化的方式是让每个类都实现Serializable接口,然后通过序列化和反序列化操作达到深克隆的目的。
package test;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.*;
@Data
@NoArgsConstructor
@AllArgsConstructor
class Person implements Serializable{
private String name;
private Integer age;
private String home;
private Friend friend;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Friend implements Serializable {
private String name;
private Integer age;
private String home;
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Person person_old = new Person();
person_old.setName("张三");
person_old.setAge(18);
person_old.setHome("chengdu");
person_old.setFriend(new Friend("李四",19,"chongqin"));
System.out.println("初始化下的person:" + person_old);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(person_old); // 序列化
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Person person_clone = (Person) objectInputStream.readObject(); //反序列化
person_clone.setName("张武");
person_clone.setAge(19);
person_clone.getFriend().setAge(20);
System.out.println("克隆出来的person:" + person_clone);
System.out.println("修改之后的person:" + person_clone);
System.out.println("修改后的原person:" + person_old);
}
}
总结:
1、 浅克隆性能相比较new会有一些提高(很小),但是编码省了很多。
2、深克隆第一种实现方式同1。
3、深克隆的第二种实现方式在数据量多的情况下性能较差,但是当类熟悉特别多、对象占用空间特别大时会有较好效果,另外通过序列化可以将对象当前的形态以文件、IO流的方式保存下来,在需要时再进行加载转换,