Java克隆介绍
在Java中存在两种克隆方式:
- 深克隆:不仅克隆对象本身,还克隆对象包含的引用所指向的所有对象。(通过实现Serilable接口)
- 浅克隆:仅克隆对象本身,不可隆对象中的引用指向的对象。(通过实现Cloneable接口)
代码实现
CloneableBO
package clone;
/**
* 通过实现Cloneable接口来克隆
*
* @author zhao.hualuo
* Create at 2021/12/31
*/
public class CloneableBO implements Cloneable {
/** 编号 */
private int id;
/** 姓名 */
private String name;
/** 对象 */
private CommonBO commonBO;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public CommonBO getCommonBO() {
return commonBO;
}
public void setCommonBO(CommonBO commonBO) {
this.commonBO = commonBO;
}
@Override
public String toString() {
return "CloneableBO{" +
"id=" + id +
", name='" + name + '\'' +
", commonBO=" + commonBO +
'}';
}
@Override
public CloneableBO clone() {
try {
CloneableBO clone = (CloneableBO) super.clone();
// TODO: copy mutable state here, so the clone can't change the internals of the original
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
CommonBO
package clone;
import java.io.Serializable;
/**
* 公共对象
*
* @author zhao.hualuo
* Create at 2021/12/31
*/
public class CommonBO implements Serializable {
/** 内容 */
private String context;
public CommonBO(String context) {
this.context = context;
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
@Override
public String toString() {
return "CommonBO{" +
"context='" + context + '\'' +
'}';
}
}
SerializableBO
package clone;
import java.io.Serializable;
/**
* 通过序列化方式实现克隆
*
* @author zhao.hualuo
* Create at 2021/12/31
*/
public class SerializableBO implements Serializable {
/** 编号 */
private int id;
/** 姓名 */
private String name;
/** 对象 */
private CommonBO commonBO;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public CommonBO getCommonBO() {
return commonBO;
}
public void setCommonBO(CommonBO commonBO) {
this.commonBO = commonBO;
}
@Override
public String toString() {
return "SerializableBO{" +
"id=" + id +
", name='" + name + '\'' +
", commonBO=" + commonBO +
'}';
}
}
SerializableUtil
package clone;
import java.io.*;
/**
* 序列化克隆工具
*
* @author zhao.hualuo
* Create at 2021/12/31
*/
public class SerializableUtil {
public static <T extends Serializable> T clone(T obj) throws IOException, ClassNotFoundException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(obj);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
return (T) objectInputStream.readObject();
}
}
MainTest
package clone;
import java.io.IOException;
/**
* 克隆工具 测试
*
* @author zhao.hualuo
* Create at 2021/12/31
*/
public class MainTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
CloneableBO cloneableBO = new CloneableBO();
cloneableBO.setId(1);
cloneableBO.setName("小赵");
cloneableBO.setCommonBO(new CommonBO("我是内容1"));
System.out.println("cloneable 复制前 初始内容:" + cloneableBO);
CloneableBO cloneableBOCopy = cloneableBO.clone();
cloneableBOCopy.getCommonBO().setContext("我是内容2");
System.out.println("cloneable 复制后 初始内容:" + cloneableBO);
System.out.println("cloneable 复制后 复制内容:" + cloneableBOCopy);
SerializableBO serializableBO = new SerializableBO();
serializableBO.setId(2);
serializableBO.setName("小李");
serializableBO.setCommonBO(new CommonBO("我是内容3"));
System.out.println("serializable 复制前 初始内容:" + serializableBO);
SerializableBO serializableBOCopy = SerializableUtil.clone(serializableBO);
serializableBOCopy.getCommonBO().setContext("我是内容4");
System.out.println("serializable 复制后 初始内容:" + serializableBO);
System.out.println("serializable 复制后 复制内容:" + serializableBOCopy);
}
}
执行结果
为什么推荐选择深克隆?
基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化。这项检查是编译器完成的,不是在运行时抛出异常,这种方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总好过把问题留到运行时。