首先需要了解对象深、浅复制的概念:
浅复制:将一个对象复制后,基本数据类型(如int,String,boolean)的变量都会重新创建,而引用类型,指向的还是原对象(或数组)所指向的。
① 拷贝时候虽然创建了新的对象,但是并没有调用构造方法,直接复制堆上面的对象
② 对象中的引用对象并没有拷贝,引用的地址还是和原对象一致
③ 基本类型或者 String 默认会拷贝
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。
实现见原型模式。
·clone()方法的使用
- 实现Cloneable接口;
- 重写clone();
- 内部调用super.clone()即Obejct.clone();
- clone()修饰符为public
public class Prototype implements Cloneable {
public Object clone() throws CloneNotSupportedException {
return (Prototype) super.clone();
}
}
·重点内容Java中如果clone为什么必须实现Cloneable接口
1. clone()是native方法,在object中申明如下:
protected native Object clone() throws CloneNotSupportedException;
2. 不实现Cloneable接口,调用clone()时会抛CloneNotSupportedException异常。
3. Cloneable接口只是一个标志类,用来标识实现该接口的类具有克隆功能
浅复制实现如下:
/* 浅复制 */
public Object clone() throws CloneNotSupportedException {
return (Prototype) super.clone();
}
深复制则可以基于Java中的序列化和反序列化实现。
1. 首先要被复制的类要实现Serializable接口,这是一个空接口,作用与Cloneable类似,表明当前类支持序列化和反序列化。
2. 定义并实现深复制方法(方法命名可以随意取),这里叫deepClone吧。
public CloneClass deepClone() throws IOException, ClassNotFoundException {
/* 写入当前对象的二进制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (CloneClass) ois.readObject();
}
代码实现如下:
/* 深复制 */
public class CloneClass implements Serializable
public Object deepClone() throws IOException, ClassNotFoundException {
/* 写入当前对象的二进制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
接下来看例子:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, CloneNotSupportedException, ClassNotFoundException {
CloneClass cloneClass = new CloneClass();
System.out.println("原对象里对象引用地址: " + cloneClass.node.toString());
cloneClass.x++;
System.out.println("原对象基本数据大小: " + cloneClass.x.toString());
CloneClass shadowClone = cloneClass.clone();
System.out.println("浅复制对象里对象引用地址: " + shadowClone.node.toString());
shadowClone.x++;
System.out.println("原对象基本数据大小: " + cloneClass.x.toString());
System.out.println("浅复制对象基本数据大小: " + shadowClone.x.toString());
CloneClass deepClone = cloneClass.deepClone();
System.out.println("深复制对象里对象引用地址: " + deepClone.node.toString());
System.out.println("深复制对象基本数据大小:" + deepClone.x.toString());
}
}
class CloneClass implements Cloneable, Serializable {
public Integer x = 1;
TreeNode node = new TreeNode(2);
@Override
public CloneClass clone() throws CloneNotSupportedException {
return (CloneClass) super.clone();
}
public CloneClass deepClone() throws IOException, ClassNotFoundException {
/* 写入当前对象的二进制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (CloneClass) ois.readObject();
}
}
class TreeNode implements Serializable {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
输出结果:
原对象里对象引用地址: com.xx.bigdata.TreeNode@6f94fa3e
原对象基本数据大小: 2
浅复制对象里对象引用地址: com.xx.bigdata.TreeNode@6f94fa3e
原对象基本数据大小: 2
浅复制对象基本数据大小: 3
深复制对象里对象引用地址: com.xx.bigdata.TreeNode@9807454
深复制对象基本数据大小:2
可以看出,浅复制只复制基本数据类型,对象引用不变,深复制会复制基本数据类型和对象。