Java中的clone()和Cloneable接口實(shí)例
clone()和Cloneable接口
clone顧名思義就是克隆,即,復(fù)制一個相等的對象,但是不同的引用地址。
我們知道拿到一個對象的地址,只要提供相應(yīng)的方法就可以修改這個對象,但是如果我們想要得到這個對象去修改它,又想保留這個對象原來的屬性,這是就可以使用clone(),它會復(fù)制一個內(nèi)容相同的對象而具有不同內(nèi)存地址。
Cloneable接口,就是我們要使用clone()必須實(shí)現(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,可以看到如果不實(shí)現(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é)果不是我們所預(yù)期的,這意味著并沒有真正克隆ChildBean,只是克隆的它的內(nèi)存地址,導(dǎo)致兩個具有相同的內(nèi)存地址,這也就是淺克隆,此時我們需要的是深克隆,需要按照下面方法修改,重寫clone()方法
@Override
public Object clone() throws CloneNotSupportedException {
Bean bean = (Bean) super.clone();
bean.childBean = (ChildBean) bean.childBean.clone();
return bean;
}
但是這樣做如果有很多層的類,那每一層都需要去重寫,顯得很麻煩。所以我們可以用下面的工具類來實(shí)現(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
這樣就可以很輕松的得到我們預(yù)期的結(jié)果,但是記得每一個類都要去 實(shí)現(xiàn)Serializable接口。
Cloneable和clone()的總結(jié)
1.Cloneable 的用途
Cloneable和Serializable一樣都是標(biāo)記型接口,它們內(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.克隆的舉例
要讓一個對象進(jìn)行克隆,其實(shí)就是兩個步驟:
1.讓該類實(shí)現(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運(yùn)用反序列化手段,調(diào)用java.io.ObjectInputStream對象的 readObject()方法。
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Security LDAP實(shí)現(xiàn)身份驗(yàn)證的項目實(shí)踐
在本文中,我們涵蓋了“使用 Spring Boot 的 Spring Security LDAP 身份驗(yàn)證示例”的所有理論和示例部分,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
Java基礎(chǔ)之Math和Random類知識總結(jié)
今天帶大家來學(xué)習(xí)java的Math和Random類,文中有非常詳細(xì)的代碼示例及介紹,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們很有幫助喲,需要的朋友可以參考下2021-05-05
Spring Boot Admin 進(jìn)行項目監(jiān)控管理的方法
Spring Boot Admin是一個開源社區(qū)項目,用于管理和監(jiān)控SpringBoot應(yīng)用程序。 這篇文章主要介紹了 Spring Boot Admin 進(jìn)行項目監(jiān)控管理的方法,需要的朋友可以參考下2020-07-07
java 中Collection存儲器詳解及簡單實(shí)例
這篇文章主要介紹了java 中Collection存儲器詳解及簡單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-04-04
如何使用Spring MVC的消息轉(zhuǎn)換器設(shè)置日期格式
這篇文章主要介紹了如何使用Spring MVC的消息轉(zhuǎn)換器設(shè)置日期格式,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07
Spring Boot中利用JavaMailSender發(fā)送郵件的方法示例(附源碼)
這篇文章主要介紹了Spring Boot中利用JavaMailSender發(fā)送郵件的方法示例, 相信使用過Spring的眾多開發(fā)者都知道Spring提供了非常好用的JavaMailSender接口實(shí)現(xiàn)郵件發(fā)送。在Spring Boot的Starter模塊中也為此提供了自動化配置。需要的朋友可以參考借鑒。2017-02-02

