浅克隆
在浅克隆中,如果原型对象的成员变量是值类型(int、double、byte、boolean、char等基本数据类型),将数据复制一份给克隆对象;如果原型对象的成员变量是引用类型(类、接口、数组等复杂数据类型),则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
深克隆
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制了一份给克隆对象。
java中的clone方法和Cloneable接口
在java中,所有类都继承了java.long.Object类,在Object类中提供了clone方法,可以将一个java对象复制一份。因此可以直接使用Object提供的clone方法实现对象的浅克隆
但有一点需要注意,java中定义了一个Cloneable的接口来标识一个类是可以进行克隆的,如果一个类没有实现Cloneable接口但调用了clone方法,会抛出CloneNotSuportedException异常
java中clone方法满足以下几点
- 对任何对象x,都有
x.clone()!=x
,及克隆对象与原型对象不是同一个对象- 对任何对象x,都有
x.clone().getClass()==x.getClass()
,即克隆对象与原型对象的类型一样
利用Object类的clone方法获取一个对象的克隆对象的步骤如下
- 在派生类中覆盖基类的clone()方法
- 在派生类的clone()方法中调用super.clone()
- 派生类需事先Cloneable接口
例子
有一个报告,报告里包含了一个附件,报告有页数属性
浅克隆实例
附件类
package cn.kevinlu98.blog.shallowclone;
/**
* @Author: Kevin·Lu
* @Date: 1:40 PM 2019/8/13
* @Description: 附件
*/
public class Enclosure {
// 附件名称
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Enclosure{" +
"name='" + name + '\'' +
'}';
}
}
报告类
package cn.kevinlu98.blog.shallowclone;
/**
* @Author: Kevin·Lu
* @Date: 1:39 PM 2019/8/13
* @Description: 报告
*/
public class Report implements Cloneable {
//页数
private int page;
//附件
private Enclosure enclosure;
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public Enclosure getEnclosure() {
return enclosure;
}
public void setEnclosure(Enclosure enclosure) {
this.enclosure = enclosure;
}
/**
* 调用Object类的clone方法实现浅拷贝
*
* @return
*/
@Override
protected Report clone() {
try {
return (Report) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("该对象不支持复制");
return null;
}
}
@Override
public String
toString() {
return "Report{" +
"page=" + page +
", enclosure=" + enclosure +
'}';
}
}
测试类
package cn.kevinlu98.blog.shallowclone;
/**
* @Author: Kevin·Lu
* @Date: 1:45 PM 2019/8/13
* @Description:
*/
public class Client {
public static void main(String[] args) {
//创建附件A
Enclosure enclosureA = new Enclosure();
enclosureA.setName("附件A");
//创建报告A
Report reportA = new Report();
reportA.setEnclosure(enclosureA);
reportA.setPage(10);
//通过clone创建报告B
Report reportB = reportA.clone();
System.out.println("reportA == reportB :" + (reportA == reportB));
System.out.println("reportA.getEnclosure() == reportB.getEnclosure() :" + (reportA.getEnclosure() == reportB.getEnclosure()));
//设置报告B的属性
reportB.getEnclosure().setName("附件B");
reportB.setPage(20);
System.out.println(reportA);
System.out.println(reportB);
}
}
结果
深拷贝实例
我们现在在刚刚代码的基础上做修改,实现深拷贝
附件类 我们需要序列化实例,所以需要继承Serializable接口
package cn.kevinlu98.blog.deepclone;
import java.io.Serializable;
/**
* @Author: Kevin·Lu
* @Date: 1:40 PM 2019/8/13
* @Description: 附件
*/
public class Enclosure implements Serializable {
// 附件名称
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Enclosure{" +
"name='" + name + '\'' +
'}';
}
}
报告类,
package cn.kevinlu98.blog.deepclone;
import java.io.*;
/**
* @Author: Kevin·Lu
* @Date: 1:39 PM 2019/8/13
* @Description: 报告
*/
public class Report implements Serializable {
//页数
private int page;
//附件
private Enclosure enclosure;
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public Enclosure getEnclosure() {
return enclosure;
}
public void setEnclosure(Enclosure enclosure) {
this.enclosure = enclosure;
}
/**
* 使用序列化技术实现深克隆
*
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public Report deepClone() throws IOException, ClassNotFoundException {
//将对象写入流中
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象从流中取出
ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Report) ois.readObject();
}
@Override
public String
toString() {
return "Report{" +
"page=" + page +
", enclosure=" + enclosure +
'}';
}
}
测试启动类
package cn.kevinlu98.blog.deepclone;
import java.io.IOException;
/**
* @Author: Kevin·Lu
* @Date: 1:45 PM 2019/8/13
* @Description:
*/
public class Client {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建附件A
Enclosure enclosureA = new Enclosure();
enclosureA.setName("附件A");
//创建报告A
Report reportA = new Report();
reportA.setEnclosure(enclosureA);
reportA.setPage(10);
//通过clone创建报告B
Report reportB = reportA.deepClone();
System.out.println("reportA == reportB :" + (reportA == reportB));
System.out.println("reportA.getEnclosure() == reportB.getEnclosure() :" + (reportA.getEnclosure() == reportB.getEnclosure()));
//设置报告B的属性
reportB.getEnclosure().setName("附件B");
reportB.setPage(20);
System.out.println(reportA);
System.out.println(reportB);
}
}
结果