Java - 关于 Cloneable 接口 clone 方法
Posted 程序员牧码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java - 关于 Cloneable 接口 clone 方法相关的知识,希望对你有一定的参考价值。
一、使用
创建对象有两种方式:new 和 clone
当一个对象创建过程复杂,我们是否可以根据已有的对象直接来克隆一份,而不必关系创建的细节呢(原型模式)
1、Java Object根类默认提供了clone方法
protected native Object clone() throws CloneNotSupportedException;
一个本地方法,protected权限: 这样做是为避免我们创建每一个类都默认具有克隆能力。
2、实现Cloneable接口
我们要使用一个对象的clone方法,必须Cloneable接口,这个接口没有任何实现,跟 Serializable一样是一种标志性接口。
如果不实现Cloneable接口,会抛出CloneNotSupportedException异常。
重写clone方法,使用public修饰(否则外部调用不到),调用父类的clone方法,如下
public class CloneModel implements Cloneable {
private String name;
private int age;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "CloneModel{" +
"name='" + name + '\\'' +
", age=" + age +
'}';
}
}
3、Object.clone() 与 构造方法
我们看一个例子 CloneModel 类
public class CloneModel implements Cloneable {
private String name;
private int age;
public CloneModel(){
System.out.println("will new a instance");
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "CloneModel{" +
"name='" + name + '\\'' +
", age=" + age +
'}';
}
}
当我们调用此对象的clone方法时,构造方法并没有被调用,所以我说创建一个对象new和clone是两条路。
public static void main(String[] args) throws CloneNotSupportedException {
CloneModel cloneModel = new CloneModel();
System.out.println(cloneModel.clone());
}
输出
CloneModel{name='null', age=0}
二、重写clone方法原则
- x.clone != x将会是true;
- x.clone().getClass()==x.getClass()将会是true(不是绝对的,但建议这么做)
- x.clone().equals(x)将会是true(不是绝对的,但建议这么做)
三、浅克隆和深克隆
1、默认clone方法时浅克隆
Object默认的clone方法实际是对域的简单拷贝,对于简单数据类型,是值的拷贝;
对于复杂类型的字段,则是指针地址的拷贝,clone后的对象和原对象指向的还是一个地址空间
所以说默认的clone方法时浅克隆。例子
class Model2{
int height;
}
public class CloneModel implements Cloneable{
private String name;
private int age;
private Model2 model2;
public CloneModel() {
this.model2 = new Model2();
}
public Model2 getModel2() {
return model2;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "CloneModel{" +
"name='" + name + '\\'' +
", age=" + age +
'}';
}
}
clone之后比较复杂对象是否相等
public static void main(String[] args) throws CloneNotSupportedException {
CloneModel cloneModel1 = new CloneModel();
CloneModel cloneModel2 = (CloneModel)cloneModel1.clone();
System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2());
}
输出
true
2、如何实现深克隆
还是上面的例子,我们改下代码
class Model2 implements Cloneable{
int height;
@Override
public Object clone() throws CloneNotSupportedException {
System.out.println("clone Model2");
return super.clone();
}
}
public class CloneModel implements Cloneable{
private String name;
private int age;
private Model2 model2;
public CloneModel() {
this.model2 = new Model2();
}
public Model2 getModel2() {
return model2;
}
public void setModel2(Model2 model2) {
this.model2 = model2;
}
@Override
public CloneModel clone() throws CloneNotSupportedException {
CloneModel cloneModelTemp = (CloneModel)super.clone();
cloneModelTemp.setModel2((Model2)cloneModelTemp.getModel2().clone());
return cloneModelTemp;
}
@Override
public String toString() {
return "CloneModel{" +
"name='" + name + '\\'' +
", age=" + age +
'}';
}
}
再次测试下
public static void main(String[] args) throws CloneNotSupportedException {
CloneModel cloneModel1 = new CloneModel();
CloneModel cloneModel2 = cloneModel1.clone();
System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2());
}
输出
false
这么做就要在super.clone的基础上 继续对非基本类型的对象递归的再次clone。显然这么方式是繁琐的且不可靠的。有没有其他的方式呢?序列化
3、序列化实现深度克隆
(1)使用java自身的序列化转为二进制数 ,再反序列化为对象
上面的例子改造下
import java.io.Serializable;
class Model2 implements Serializable {
int height;
}
public class CloneModel implements Serializable {
private String name;
private int age;
private Model2 model2;
public CloneModel() {
this.model2 = new Model2();
}
public Model2 getModel2() {
return model2;
}
}
测试代码
import com.yangfei.test.CloneModel;
import java.io.*;
public class YfTest {
public static <T extends Serializable> T deepCloneObject(T object) throws IOException {
T deepClone = null;
ObjectInputStream ois = null;
try(ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
)
{
oos.writeObject(object);
ByteArrayInputStream bais = new ByteArrayInputStream(baos
.toByteArray());
ois = new ObjectInputStream(bais);
deepClone = (T)ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null){
ois.close();
}
}
return deepClone;
}
public static void main(String[] args) throws IOException {
CloneModel cloneModel1 = new CloneModel();
CloneModel cloneModel2 = deepCloneObject(cloneModel1);
System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2());
}
}
输出
false
(2)其他序列化方式,如对象序列化json字符串再反序列化为对象
我们使用google的gson来进行序列化,上代码
import com.google.gson.Gson;
import java.io.Serializable;
class Model2 implements Serializable {
int height;
}
public class CloneModel implements Serializable {
private String name;
private int age;
private Model2 model2;
public CloneModel() {
this.model2 = new Model2();
}
public Model2 getModel2() {
return model2;
}
public CloneModel deepClone() {
Gson gson = new Gson();
return gson.fromJson(gson.toJson(this), CloneModel.class);
}
}
测试代码
public static void main(String[] args) throws IOException {
CloneModel cloneModel1 = new CloneModel();
CloneModel cloneModel2 = cloneModel1.deepClone();
System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2());
}
输出
false
(3)性能对比测试
上代码
public static void main(String[] args) throws IOException {
CloneModel cloneModel1 = new CloneModel();
long time1 = System.currentTimeMillis();
for(int i=0;i<1000;i++){
// CloneModel cloneModel2 = cloneModel1.deepClone();
CloneModel cloneModel2 = deepCloneObject(cloneModel1);
}
long time2 = System.currentTimeMillis();
System.out.println((time2-time1)+"ms");
}
对比(1000次) | Serializable | GSON |
---|---|---|
易用性 | 对象要实现Serializable,依赖的子元素依然要实现此接口,不易扩展 | 无要求,额外的工具控制,易用使用 |
性能对比 | 1000次clone耗时118ms,稍好 | 1000次clone耗时167ms |
以上是关于Java - 关于 Cloneable 接口 clone 方法的主要内容,如果未能解决你的问题,请参考以下文章