Java在复制一个对象时有浅拷贝与深拷贝之分,具体区别就不在此赘述,本文主要分析Java深拷贝的几种方法以及他们的效率高低。
1. 使用Java序列化方法
想要深拷贝一个对象,常用的方法是序列化为数据流,此方法的前提是对象以及对象中包含的子对象都要继承Serializable接口。
2. 利用Kryo序列化框架
Kryo是一个快速高效的Java序列化框架,旨在提供快速、高效和易用的API。无论文件、数据库或网络数据Kryo都可以随时完成序列化。Kryo还可以执行自动深拷贝(克隆)、浅拷贝(克隆)。这是对象到对象的直接拷贝,非对象->字节->对象的拷贝。该方法不需要继承Serializable接口。
1 2 3 4 5
| <dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>4.0.1</version> </dependency>
|
3. 利用Json转化的方法
如果对象没有继承Serializable接口,可以先将对象转化为JSON,再序列化为对象,和第一种方法类似。Json转换工具可以用Jackson或者Json-lib,本文选择Json-lib只需要在maven里面添加以下依赖:
1 2 3 4 5 6
| <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> <classifier>jdk15</classifier> </dependency>
|
4. 手动New对象的方法
人工构建对象,如果需要复制的对象中包含非基本类型,如List,对象等结构时,可以在需要的时候手动new对象,将属性值挨个调用set方法,比较繁琐。
5. 具体实例
(1) 首先构造两个实体对象User.java和Name.java,具体代码如下
1 2 3 4 5 6 7 8
| public class User implements Serializable{ private static final long serialVersionUID = -6952319891279734655L; private Name name; private String phone; private String sex; private int age; ... }
|
1 2 3 4 5 6
| public class Name implements Serializable{ private static final long serialVersionUID = -2023200990550843496L; private String firstName; private String lastName; ... }
|
(2) 测试的主程序代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
| package com.test.sort.hello;
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;
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 { } catch (Exception e) { e.printStackTrace(); } } Long after=System.currentTimeMillis();
System.out.println(after-before);
}
@SuppressWarnings("unchecked") public static <T> 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) { } } }
@SuppressWarnings("unchecked") public static <T> T copyByJson(T obj) throws Exception { return (T)JSONObject.toBean(JSONObject.fromObject(obj),obj.getClass()); }
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; }
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的序列化操作,不建议用此种方案进行深度拷贝。
参考文献
http://blog.csdn.net/isea533/article/details/9375907
https://github.com/EsotericSoftware/kryo
镜像地址
https://www.cnblogs.com/coderzhw/p/11094284.html