Java中的clone()和Cloneable接口實例
clone()和Cloneable接口
clone顧名思義就是克隆,即,復制一個相等的對象,但是不同的引用地址。
我們知道拿到一個對象的地址,只要提供相應的方法就可以修改這個對象,但是如果我們想要得到這個對象去修改它,又想保留這個對象原來的屬性,這是就可以使用clone(),它會復制一個內(nèi)容相同的對象而具有不同內(nèi)存地址。
Cloneable接口,就是我們要使用clone()必須實現(xiàn)的接口,不然會拋出異常。
public class Bean implements Cloneable { private String a; public Bean(String a) { this.a = a; } public String getA() { return a; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public boolean equals(Object o) { if(o instanceof Bean){ Bean bean = (Bean) o; return bean.getA().equals(a); } return false; } }
在Cloneable 接口中并沒有給我們定義任何方法
這里需要重寫clone()方法
protected native Object clone() throws CloneNotSupportedException; protected Object clone() throws CloneNotSupportedException { if (!(this instanceof Cloneable)) { throw new CloneNotSupportedException("Class " + getClass().getName() + " doesn't implement Cloneable"); } return internalClone(); }
它是Object類里面的native方法,它是protected的,根據(jù)需要可以寫為public,可以看到如果不實現(xiàn)Cloneable接口將會拋出CloneNotSupportedException 異常。
測試一下
try { Bean a = new Bean("lzy"); Bean b = a; Bean c = (Bean) a.clone(); Log.i(TAG, "onCreate: " + (a == b)); //true Log.i(TAG, "onCreate: " + (a.equals(b))); //true Log.i(TAG, "onCreate: " + (a == c)); //false Log.i(TAG, "onCreate: " + (a.equals(c))); //true } catch (CloneNotSupportedException e) { e.printStackTrace(); }
可以看到克隆出來的類的地址是不同的,而內(nèi)容是相同的。
下面修改一下,在Bean加一個成員變量ChildBean
public class ChildBean implements Cloneable { private String c; public String getC() { return c; } public ChildBean(String c) { this.c = c; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public boolean equals(Object o) { if (o instanceof ChildBean) { ChildBean bean = (ChildBean) o; return bean.getC().equals(c); } return false; } }
public class Bean implements Cloneable { private String a; private ChildBean childBean; public Bean(String a, ChildBean childBean) { this.a = a; this.childBean = childBean; } public String getA() { return a; } public ChildBean getChildBean() { return childBean; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public boolean equals(Object o) { if (o instanceof Bean) { Bean bean = (Bean) o; return bean.getA().equals(a); } return false; } }
Bean a = new Bean("lzy", new ChildBean("child")); Bean b = a; Bean c = (Bean) a.clone(); Log.i(TAG, "onCreate: " + (a.getChildBean() == b.getChildBean())); //true Log.i(TAG, "onCreate: " + (a.getChildBean().equals(b.getChildBean()))); //true Log.i(TAG, "onCreate: " + (a.getChildBean() == c.getChildBean())); //true Log.i(TAG, "onCreate: " + (a.getChildBean().equals(c.getChildBean()))); //true
測試發(fā)現(xiàn)有一個結(jié)果不是我們所預期的,這意味著并沒有真正克隆ChildBean,只是克隆的它的內(nèi)存地址,導致兩個具有相同的內(nèi)存地址,這也就是淺克隆,此時我們需要的是深克隆,需要按照下面方法修改,重寫clone()方法
@Override public Object clone() throws CloneNotSupportedException { Bean bean = (Bean) super.clone(); bean.childBean = (ChildBean) bean.childBean.clone(); return bean; }
但是這樣做如果有很多層的類,那每一層都需要去重寫,顯得很麻煩。所以我們可以用下面的工具類來實現(xiàn)
public class BeanUtil { public static <T> T cloneTo(T src) throws RuntimeException { ByteArrayOutputStream memoryBuffer = new ByteArrayOutputStream(); ObjectOutputStream out = null; ObjectInputStream in = null; T dist = null; try { out = new ObjectOutputStream(memoryBuffer); out.writeObject(src); out.flush(); in = new ObjectInputStream(new ByteArrayInputStream(memoryBuffer.toByteArray())); dist = (T) in.readObject(); } catch (Exception e) { throw new RuntimeException(e); } finally { if (out != null) try { out.close(); out = null; } catch (IOException e) { throw new RuntimeException(e); } if (in != null) try { in.close(); in = null; } catch (IOException e) { throw new RuntimeException(e); } } return dist; } }
Bean a = new Bean("lzy", new ChildBean("child")); Bean b = BeanUtil.cloneTo(a); Log.i(TAG, "onCreate: " + (a.getChildBean() == b.getChildBean())); //false Log.i(TAG, "onCreate: " + (a.getChildBean().equals(b.getChildBean()))); //true
這樣就可以很輕松的得到我們預期的結(jié)果,但是記得每一個類都要去 實現(xiàn)Serializable接口。
Cloneable和clone()的總結(jié)
1.Cloneable 的用途
Cloneable和Serializable一樣都是標記型接口,它們內(nèi)部都沒有方法和屬性,implements Cloneable表示該對象能被克隆,能使用Object.clone()方法。如果沒有implements Cloneable的類調(diào)用Object.clone()方法就會拋出CloneNotSupportedException。
2.克隆的分類
(1)淺克?。╯hallow clone),淺拷貝是指拷貝對象時僅僅拷貝對象本身和對象中的基本變量,而不拷貝對象包含的引用指向的對象。
(2)深克?。╠eep clone),深拷貝不僅拷貝對象本身,而且拷貝對象包含的引用指向的所有對象。
舉例區(qū)別一下:對象A1中包含對B1的引用,B1中包含對C1的引用。淺拷貝A1得到A2,A2中依然包含對B1的引用,B1中依然包含對C1的引用。深拷貝則是對淺拷貝的遞歸,深拷貝A1得到A2,A2中包含對B2(B1的copy)的引用,B2中包含對C2(C1的copy)的引用。
3.克隆的舉例
要讓一個對象進行克隆,其實就是兩個步驟:
1.讓該類實現(xiàn)java.lang.Cloneable接口;
2.重寫(override)Object類的clone()方法。
public class Wife implements Cloneable { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Wife(int id,String name) { this.id = id; this.name = name; } @Override public int hashCode() {//myeclipse自動生成的 final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) {//myeclipse自動生成的 if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Wife other = (Wife) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * @param args * @throws CloneNotSupportedException */ public static void main(String[] args) throws CloneNotSupportedException { Wife wife = new Wife(1,"wang"); Wife wife2 = null; wife2 = (Wife) wife.clone(); System.out.println("class same="+(wife.getClass()==wife2.getClass()));//true System.out.println("object same="+(wife==wife2));//false System.out.println("object equals="+(wife.equals(wife2)));//true } }
4.淺克隆的舉例
public class Husband implements Cloneable { private int id; private Wife wife; public Wife getWife() { return wife; } public void setWife(Wife wife) { this.wife = wife; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Husband(int id) { this.id = id; } @Override public int hashCode() {//myeclipse自動生成的 final int prime = 31; int result = 1; result = prime * result + id; return result; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public boolean equals(Object obj) {//myeclipse自動生成的 if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Husband other = (Husband) obj; if (id != other.id) return false; return true; } /** * @param args * @throws CloneNotSupportedException */ public static void main(String[] args) throws CloneNotSupportedException { Wife wife = new Wife(1,"jin"); Husband husband = new Husband(1); Husband husband2 = null; husband.setWife(wife); husband2 = (Husband) husband.clone(); System.out.println("husband class same="+(husband.getClass()==husband2.getClass()));//true System.out.println("husband object same="+(husband==husband2));//false System.out.println("husband object equals="+(husband.equals(husband)));//true System.out.println("wife class same="+(husband.getWife().getClass()==husband2.getWife().getClass()));//true System.out.println("wife object same="+(husband.getWife()==husband2.getWife()));//true System.out.println("wife object equals="+(husband.getWife().equals(husband.getWife())));//true } }
5.深克隆的舉例
如果要深克隆,需要重寫(override)Object類的clone()方法,并且在方法內(nèi)部調(diào)用持有對象的clone()方法;注意如下代碼的clone()方法
public class Husband implements Cloneable { private int id; private Wife wife; public Wife getWife() { return wife; } public void setWife(Wife wife) { this.wife = wife; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Husband(int id) { this.id = id; } @Override public int hashCode() {//myeclipse自動生成的 final int prime = 31; int result = 1; result = prime * result + id; return result; } @Override protected Object clone() throws CloneNotSupportedException { Husband husband = (Husband) super.clone(); husband.wife = (Wife) husband.getWife().clone(); return husband; } @Override public boolean equals(Object obj) {//myeclipse自動生成的 if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Husband other = (Husband) obj; if (id != other.id) return false; return true; } /** * @param args * @throws CloneNotSupportedException */ public static void main(String[] args) throws CloneNotSupportedException { Wife wife = new Wife(1,"jin"); Husband husband = new Husband(1); Husband husband2 = null; husband.setWife(wife); husband2 = (Husband) husband.clone(); System.out.println("husband class same="+(husband.getClass()==husband2.getClass()));//true System.out.println("husband object same="+(husband==husband2));//false System.out.println("husband object equals="+(husband.equals(husband)));//true System.out.println("wife class same="+(husband.getWife().getClass()==husband2.getWife().getClass()));//true System.out.println("wife object same="+(husband.getWife()==husband2.getWife()));//false System.out.println("wife object equals="+(husband.getWife().equals(husband.getWife())));//true } }
但是也有不足之處,如果Husband內(nèi)有N個對象屬性,突然改變了類的結(jié)構(gòu),還要重新修改clone()方法。
解決辦法:可以使用Serializable運用反序列化手段,調(diào)用java.io.ObjectInputStream對象的 readObject()方法。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
JFINAL+Ajax傳參 array 數(shù)組方法 獲取request中數(shù)組操作
這篇文章主要介紹了JFINAL+Ajax傳參 array 數(shù)組方法 獲取request中數(shù)組操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08SpringBoot+MyBatis實現(xiàn)登錄案例
前端時間在網(wǎng)上看到有朋友在學習springboot項目的搭建過程,今天就抽空給大家分享一個案例幫助大家學習SpringBoot+MyBatis實現(xiàn)登錄功能,具體實現(xiàn)代碼跟隨小編一起看看吧2021-06-06詳解Mybatis中的 ${} 和 #{}區(qū)別與用法
這篇文章主要介紹了Mybatis中的 ${} 和 #{}區(qū)別與用法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07