Object 的 clone() 方法是浅层复制(可是 native 很高效)。
另外,Java 提供了数组和集合的复制方法,分别是 Arrays.copy() 和 Collections.copy() 方法。
前者实际上使用了 System.arraycopy() 方法,二者其实也是浅层复制,过程相似于下面的 for 循环:数组
for(int i=0; i<len; i++){ dest[i] = src[i]; }
因此当数组或集合中元素是对象时,只是作了引用的复制,指向的仍是堆中同一个对象。ide
1)实现 Cloneable 接口性能
包装 Object.clone() ,根据属性类型深度 clone。this
这种方法,使用了 Object.clone() ,优势是 native 方法性能好,缺点是实现太繁琐。spa
/** * 0 实现 Cloneable 接口 * 1 包装 super.clone(),提供 public 方法 * 2 默认的是浅层复制 * @author Super * */ public class shallowCloneTest { public static void main(String[] args) { Resource0 r0 = new Resource0(0, "资源1号"); Resource0 r1 = new Resource0(1, "内部资源"); r0.setInnerResource(r1); //验证克隆 Resource0 r2 = r0.shallowClone(); System.out.println(r0); System.out.println(r2); System.out.println(r1==r2); //false //验证浅度克隆 r2.getInnerResource().setId(7); System.out.println(r1); //受影响了 } } /** * 深层复制方案一:包装 clone,引用变量继续 clone * @author Super * */ public class DeepCloneTest1 { public static void main(String[] args) { Resource0 r0 = new Resource0(0, "资源1号"); Resource0 r1 = new Resource0(1, "内部资源"); r0.setInnerResource(r1); //验证克隆 Resource0 r2 = r0.deepClone(); System.out.println(r0); System.out.println(r2); System.out.println(r1==r2); //false //验证深度度克隆 r2.getInnerResource().setId(7); System.out.println(r1); //不受影响 } } public class Resource0 extends BaseVo implements Cloneable { private static final long serialVersionUID = 1L; private Integer id; private String name; private Resource0 innerResource; public Resource0(Integer id, String name) { super(); this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Resource0 getInnerResource() { return innerResource; } public void setInnerResource(Resource0 innerResource) { this.innerResource = innerResource; } /** * 浅层复制 * @return */ public Resource0 shallowClone(){ Resource0 r = null; try { r = (Resource0)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return r; } /** * 深层复制 * @return */ public Resource0 deepClone(){ Resource0 r = null; try { r = (Resource0)super.clone(); r.innerResource = (Resource0) innerResource.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return r; } @Override public String toString() { return "Resource0 [id=" + id + ", name=" + name + ", innerResource=" + innerResource + "]"; } }
2)对象序列化输入输出code
这种方法,强大且简单,先写到内存,再读出来。对象
PS:单例对象最好也关注下是否有被序列化复制的风险。blog
/** * 深层复制方案2:输入输出序列化 * @author Super * */ public class DeepCloneTest2 { public static void main(String[] args) { Resource0 r0 = new Resource0(0, "资源1号"); Resource0 r1 = new Resource0(1, "内部资源"); r0.setInnerResource(r1); //验证克隆 Resource0 r2 = (Resource0) IOUtil.deepClone(r0); System.out.println(r0); System.out.println(r2); System.out.println(r1==r2); //false //验证深度度克隆 r2.getInnerResource().setId(7); System.out.println(r1); //不受影响 } } /** * 深层复制序列化 vo * @param src * @return dest * @throws IOException * @throws ClassNotFoundException */ public static BaseVo deepClone(BaseVo src) { ByteArrayOutputStream bo = null; ObjectOutputStream out = null; ObjectInputStream in = null; BaseVo dest = null; try{ try{ //对象写入内存 bo = new ByteArrayOutputStream(); out = new ObjectOutputStream(bo); out.writeObject(src); //从内存中读回来 in = new ObjectInputStream(new ByteArrayInputStream(bo.toByteArray())); dest = (BaseVo) in.readObject(); }finally{ //使用 finally 关闭资源 if(in!=null){ in.close(); } if(out!=null){ out.close(); } if(bo!=null){ bo.close(); } } //使用 catch 块统一捕捉资源 } catch(IOException | ClassNotFoundException ex){ ex.printStackTrace(); } return dest; }
参考阅读:接口
https://www.jianshu.com/p/7aaaf884cc44内存