某个同学希望某个类可被clone,该类其实继承HashMap,本身有clone,但是该同事不知道,所以写了一个clone方法
@Override
public Object clone() {
return SerializationUtils.clone(this);
}
结果他的代码正常,其它地方在clone的时候,报了另外一个类java.io.NotSerializableException
原因分析1.该类原先继承HashMap,所以原来clone是调用HashMap的clone(value值为浅拷贝)
2.该类的clone方法被同学覆盖,使用的是SerializationUtils.clone(深拷贝)
3.SerializationUtils.clone方法需要类实现Serializable,并且如果其包含的属性非原生数据类型,其属性类也需要实现Serializable
如果项目只需要浅拷贝,获取值,而不改变值里面的内容,则可以使用HashMap的clone方法
解决方案二如果项目需要做深拷贝,可能改变值里面的内容,那么需要重写clone方法,可以SerializationUtils.clone,但是类及其子类都需要实现Serializable接口
总结思考 浅拷贝&深拷贝浅拷贝:如果基本数据类型则拷贝一份,非基本类型(包括数组)都只是拷贝引用,所以非基本数据类型原始值修改,则拷贝也会修改
深拷贝:无论是否基本数据类型都会做一份拷贝
hashmap的拷贝,看代码
public Object clone() {
HashMap result;
try {
result = (HashMap)super.clone();
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
result.reinitialize();
result.putMapEntries(this, false);
return result;
}
HashMap的clone方法,map会生成新的对象,但新的HashMap所存储的引用类型还是原来Map的值,hashmap的clone本身map会复制一份,但是其value值是一个浅拷贝。
浅拷贝实现方式 通过实现Cloneable接口java实现一个类的浅拷贝,则只需要实现Cloneable接口,同时覆盖重新Objec的clone方法即可
public class MyClass implements Cloneable{
Integer num;
MySubClass mySubClass;
public MyClass(Integer num, MySubClass mySubClass) {
this.num = num;
this.mySubClass = mySubClass;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
@Override
public MyClass clone() throws CloneNotSupportedException {
return (MyClass)super.clone();
}
}
为什么要实现Cloneable接口,这个接口没有任何方法;其实只是打上一个标识,在Object的native clone方法中有一段代码就是判断是否实现该接口。可能是为了表示有些类可以clone,有些类不能clone吧。
通过BeanUtils使用apache的Common BeanUtils,pom文件引入
commons-beanutils commons-beanutils 1.9.4
代码如下:
MySubClass mySubClass1 = (MySubClass)BeanUtils.cloneBean(mySubClass); System.out.println(mySubClass1);
其原理是通过反射创建一个新对象,然后通过copyProperties方法将类的值一个个拷贝,因此是一个浅拷贝
深拷贝实现方式当然深拷贝有自身实现的方式,比如还是继续重写clone方法,在clone方法中将非基本数据类型new出一个新对象,子对象也是重新clone方法,这样会比较繁琐。这里有些工具类可供选择
Apache Commons Lang序列化方式深拷贝MyClass myClass = new MyClass();//该类必须实现Serializable接口 SerializationUtils.colne(myClass);
其原理是通序列化和反序列化的方式,重新生成对象。不过需要每个类都实现Serializable接口
Gson或者JackJson序列化方式深拷贝其原理也差不多,是同步序列化成json对象,再反序列化。



