java在复制一个对象时有浅拷贝与深拷贝之分,具体区别就不在此赘述,本文主要分析java深拷贝的几种方法以及他们的效率高低。
1. 使用java序列化方法
想要深拷贝一个对象,常用的方法是序列化为数据流,此方法的前提是对象以及对象中包含的子对象都要继承serializable接口。
2. 利用kryo序列化框架
kryo是一个快速高效的java序列化框架,旨在提供快速、高效和易用的api。无论文件、数据库或网络数据kryo都可以随时完成序列化。kryo还可以执行自动深拷贝(克隆)、浅拷贝(克隆)。这是对象到对象的直接拷贝,非对象->字节->对象的拷贝。该方法不需要继承serializable接口。
com.esotericsoftware
kryo
4.0.1
3. 利用json转化的方法
如果对象没有继承serializable接口,可以先将对象转化为json,再序列化为对象,和第一种方法类似。json转换工具可以用jackson或者json-lib,本文选择json-lib只需要在maven里面添加以下依赖:
net.sf.json-lib
json-lib
2.4
jdk15
4. 手动new对象的方法
人工构建对象,如果需要复制的对象中包含非基本类型,如list,对象等结构时,可以在需要的时候手动new对象,将属性值挨个调用set方法,比较繁琐。
5. 具体实例
(1) 首先构造两个实体对象user.java和name.java,具体代码如下
public class user implements serializable{
private static final long serialversionuid = -6952319891279734655l;
private name name;
private string phone;
private string sex;
private int age;
...//此处省略get、set方法
}
public class name implements serializable{
private static final long serialversionuid = -2023200990550843496l;
private string firstname;
private string lastname;
...//此处省略get、set方法
}
(2) 测试的主程序代码如下:
package com.test.sort.hello;
/**
* created by geekboy on 2017/12/10.
*/
import java.io.bytearrayinputstream;
import java.io.bytearrayoutputstream;
import java.io.objectinputstream;
import java.io.objectoutputstream;
import com.esotericsoftware.kryo.kryo;
import net.sf.json.jsonobject;
/**
* @author geekboy
*
*/
public class testcopy {
public static void main(string[] args) {
user source=new user();
source.setage(25);
source.setphone("13590117892");
source.setsex("1");
name name=new name();
name.setfirstname("li");
name.setlastname("ming");
source.setname(name);
long before=system.currenttimemillis();
for(int i=0;i<1000000;i++){
user tmp=copybynewobject(source);
try {
//user tmp=copyimplserializable(source);
//user tmp=copybyjson(source);
} catch (exception e) {
e.printstacktrace();
}
}
long after=system.currenttimemillis();
system.out.println(after-before);
}
/**
* 深层拷贝 - 需要类继承序列化接口
* @param
* @param obj
* @return
* @throws exception
*/
@suppresswarnings("unchecked")
public static t copyimplserializable(t obj) throws exception {
bytearrayoutputstream baos = null;
objectoutputstream oos = null;
bytearrayinputstream bais = null;
objectinputstream ois = null;
object o = null;
//如果子类没有继承该接口,这一步会报错
try {
baos = new bytearrayoutputstream();
oos = new objectoutputstream(baos);
oos.writeobject(obj);
bais = new bytearrayinputstream(baos.tobytearray());
ois = new objectinputstream(bais);
o = ois.readobject();
return (t) o;
} catch (exception e) {
throw new exception("对象中包含没有继承序列化的对象");
} finally{
try {
baos.close();
oos.close();
bais.close();
ois.close();
} catch (exception e2) {
//这里报错不需要处理
}
}
}
/**
* 深层拷贝 - 需要net.sf.json.jsonobject
* @param
* @param obj
* @return
* @throws exception
*/
@suppresswarnings("unchecked")
public static t copybyjson(t obj) throws exception {
return (t)jsonobject.tobean(jsonobject.fromobject(obj),obj.getclass());
}
/**
* 通过new 对象的方法深拷贝
* @param source
* @return
*/
public static user copybynewobject(user source){
user result=new user();
result.setage(source.getage());
result.setphone(source.getphone());
result.setsex(source.getsex());
name name=new name();
name.setfirstname(source.getname().getfirstname());
name.setlastname(source.getname().getlastname());
result.setname(name);
return result;
}
/**
* 通过kryo框架深拷贝
* @param source
* @return
*/
public static user copybykryo(user source){
kryo kryo = new kryo();
return kryo.copy(source);
}
}
6. 运行结果及效率分析
new 对象
jdk序列化
kyro序列化
json转化
1000次
1
237
307
477
10000次
3
914
790
1328
100000次
10
2951
1780
2604
通过上面的测试可以看出new 对象的方法是最快的,比较适合性能要求较高的场合。其次是kyro序列化方法,json转化的方法还有优化的余地,使用不同的json库会有不同结果。最慢的是jdk的序列化操作,不建议用此种方案进行深度拷贝。
参考文献
希望与广大网友互动??
点此进行留言吧!