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內存