欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中對(duì)象的克隆詳解

 更新時(shí)間:2023年08月02日 08:51:49   作者:王雀躍  
這篇文章主要介紹了Java中對(duì)象的克隆詳解,Java中對(duì)象的復(fù)制分為淺復(fù)制與深復(fù)制的不同之處就在于深復(fù)制還會(huì)復(fù)制對(duì)象的引用對(duì)象,需要的朋友可以參考下

Java對(duì)象克隆(復(fù)制)

假如想復(fù)制一個(gè)簡(jiǎn)單變量。很簡(jiǎn)單:

int apples = 5;
int pears = apples;

不僅int類(lèi)型,其它七種原始數(shù)據(jù)類(lèi)型(boolean,char,byte,short,float,double.long)同樣適用于該類(lèi)情況。

但是如果你復(fù)制的是一個(gè)對(duì)象,情況就復(fù)雜了。

假設(shè)說(shuō)我是一個(gè)beginner,我會(huì)這樣寫(xiě):

class Student {  
    private int number;  
    public int getNumber() {  
        return number;  
    }  
    public void setNumber(int number) {  
        this.number = number;  
    }  
}  
public class Test {  
    public static void main(String args[]) {  
        Student stu1 = new Student();  
        stu1.setNumber(12345);  
        Student stu2 = stu1;  
        System.out.println("學(xué)生1:" + stu1.getNumber());  
        System.out.println("學(xué)生2:" + stu2.getNumber());  
    }  
} 

結(jié)果:

學(xué)生1:12345  

學(xué)生2:12345  

這里自定義學(xué)生類(lèi),該類(lèi)只有number字段。

我們新建了一個(gè)學(xué)生實(shí)例,然后將該值賦值給stu2實(shí)例。(Student stu2 = stu1;)

再看看打印結(jié)果,作為一個(gè)新手,拍了拍胸腹,對(duì)象復(fù)制不過(guò)如此,

難道真的是這樣嗎?

我們?cè)囍淖僺tu2實(shí)例的number字段,再打印結(jié)果看看:

stu2.setNumber(54321);  
System.out.println("學(xué)生1:" + stu1.getNumber()); // 學(xué)生1:54321  
System.out.println("學(xué)生2:" + stu2.getNumber()); // 學(xué)生2:54321  

為什么改變學(xué)生2的學(xué)號(hào),學(xué)生1的學(xué)號(hào)也發(fā)生變化?

原因出在(stu2 = stu1) 這一句。該語(yǔ)句是將stu1的引用賦值給stu2,

這樣,stu1和stu2指向內(nèi)存堆中同一個(gè)對(duì)象。如圖:

那么,怎樣才能達(dá)到復(fù)制一個(gè)對(duì)象呢?

是否記得萬(wàn)類(lèi)之王Object。它有11個(gè)方法,有兩個(gè)protected的方法,其中一個(gè)為clone方法。

在Java中所有的類(lèi)都是繼承自Java語(yǔ)言包中的Object類(lèi)的,查看它的源碼,發(fā)現(xiàn)里面有一個(gè)訪(fǎng)問(wèn)限定符為protected的方法clone():

/*
Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native Object clone() throws CloneNotSupportedException;

仔細(xì)一看,它還是一個(gè)native方法,大家都知道native方法是非Java語(yǔ)言實(shí)現(xiàn)的代碼,供Java程序調(diào)用的,因?yàn)镴ava程序運(yùn)行在JVM虛擬機(jī)上,要想訪(fǎng)問(wèn)比較底層與操作系統(tǒng)相關(guān)的就沒(méi)辦法,只能由靠近操作系統(tǒng)的語(yǔ)言來(lái)實(shí)現(xiàn)。

  1. 第一次聲明保證克隆對(duì)象將有單獨(dú)的內(nèi)存地址分配。
  2. 第二次聲明表明,原始和克隆的對(duì)象應(yīng)該具有相同的類(lèi)類(lèi)型,但它不是強(qiáng)制性的。
  3. 第三聲明表明,原始和克隆的對(duì)象應(yīng)該是平等的equals()方法使用,但它不是強(qiáng)制性的。

因?yàn)槊總€(gè)類(lèi)直接或間接的父類(lèi)都是Object,因此它們都含有clone()方法,但是因?yàn)樵摲椒ㄊ莗rotected,所以都不能在類(lèi)外進(jìn)行訪(fǎng)問(wèn)。

要想對(duì)一個(gè)對(duì)象進(jìn)行復(fù)制,就需對(duì)clone方法覆蓋。

為什么要克隆?

為什么需要克隆對(duì)象?直接new一個(gè)對(duì)象不行嗎?

答案是:克隆的對(duì)象可能包含一些已修改過(guò)的屬性,而new出來(lái)的對(duì)象的屬性都是初始化時(shí)的值,所以當(dāng)需要一個(gè)新的對(duì)象來(lái)保存當(dāng)前對(duì)象的“狀態(tài)”就靠clone方法。

那把這個(gè)對(duì)象的臨時(shí)屬性一個(gè)個(gè)賦值給新new的對(duì)象不也行嘛?可以是可以,但是一來(lái)麻煩不說(shuō),二來(lái),通過(guò)上面的源碼都發(fā)現(xiàn)clone是一個(gè)native方法,就是快,在底層實(shí)現(xiàn)。

提個(gè)醒,我們常見(jiàn)的Object a=new Object();Object b;b=a;這種形式的代碼復(fù)制的是引用,即對(duì)象在內(nèi)存中的地址,a和b對(duì)象仍指向同一個(gè)對(duì)象。

而通過(guò)clone方法賦值的對(duì)象跟原來(lái)的對(duì)象是同時(shí)獨(dú)立存在的。

如何實(shí)現(xiàn)克隆

介紹下兩種不同的克隆方法,淺克隆(ShallowClone)深克隆(DeepClone)。

Java語(yǔ)言中,數(shù)據(jù)類(lèi)型分為值類(lèi)型(基本數(shù)據(jù)類(lèi)型)和引用類(lèi)型,值類(lèi)型包括int、double、byte、boolean、char等簡(jiǎn)單數(shù)據(jù)類(lèi)型,引用類(lèi)型包括類(lèi)、接口、數(shù)組等復(fù)雜類(lèi)型。淺克隆和深克隆的主要區(qū)別在于是否支持引用類(lèi)型的成員變量的復(fù)制,下面將對(duì)兩者進(jìn)行詳細(xì)介紹。

一般步驟是(淺克隆):

1. 被復(fù)制的類(lèi)需實(shí)現(xiàn)Clonenable接口(不實(shí)現(xiàn)的話(huà)在調(diào)用clone方法會(huì)拋出CloneNotSupportedException異常), 該接口為標(biāo)記接口(不含任何方法)

2. 覆蓋clone()方法,訪(fǎng)問(wèn)修飾符設(shè)為public。方法中調(diào)用super.clone()方法得到需要的復(fù)制對(duì)象。(native為本地方法)

下面對(duì)上面那個(gè)方法進(jìn)行改造:

class Student implements Cloneable{  
    private int number;  
    public int getNumber() {  
        return number;  
    }  
    public void setNumber(int number) {  
        this.number = number;  
    }  
    @Override  
    public Object clone() {  
        Student stu = null;  
        try{  
            stu = (Student)super.clone();  
        }catch(CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
        return stu;  
    }  
}  
public class Test {  
    public static void main(String args[]) {  
        Student stu1 = new Student();  
        stu1.setNumber(12345);  
        Student stu2 = (Student)stu1.clone();  
        System.out.println("學(xué)生1:" + stu1.getNumber()); // 學(xué)生1:12345  
        System.out.println("學(xué)生2:" + stu2.getNumber()); // 學(xué)生2:12345  
        stu2.setNumber(54321);  
        System.out.println("學(xué)生1:" + stu1.getNumber()); // 學(xué)生1:12345  
        System.out.println("學(xué)生2:" + stu2.getNumber()); // 學(xué)生2:54321
    }  
} 

如果還不相信這兩個(gè)對(duì)象不是同一個(gè)對(duì)象,可以看看這一句:

System.out.println(stu1 == stu2); // false  

上面被稱(chēng)為淺克隆。

還有一種復(fù)雜的深度復(fù)制:

我們?cè)趯W(xué)生類(lèi)里再加一個(gè)Address類(lèi)。

class Address  {
    private String add;
    public String getAdd() {
        return add;
    }
    public void setAdd(String add) {
        this.add = add;
    }
}
class Student implements Cloneable{
    private int number;
    private Address addr;
    public Address getAddr() {
        return addr;
    }
    public void setAddr(Address addr) {
        this.addr = addr;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    @Override
    public Object clone() {
        Student stu = null;
        try{
            stu = (Student)super.clone();
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }
}
public class Test {
    public static void main(String args[]) {
        Address addr = new Address();
        addr.setAdd("杭州市");
        Student stu1 = new Student();
        stu1.setNumber(123);
        stu1.setAddr(addr);
        Student stu2 = (Student)stu1.clone();
        System.out.println("學(xué)生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
        System.out.println("學(xué)生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
    }
}

結(jié)果:

學(xué)生1:123,地址:杭州市  
 
學(xué)生2:123,地址:杭州市

乍一看沒(méi)問(wèn)題,真的是這樣嗎?在main方法中改變addr實(shí)例的地址。

addr.setAdd("西湖區(qū)");  
System.out.println("學(xué)生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
System.out.println("學(xué)生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  

結(jié)果:

學(xué)生1:123,地址:杭州市  
學(xué)生2:123,地址:杭州市  
學(xué)生1:123,地址:西湖區(qū)  
學(xué)生2:123,地址:西湖區(qū) 

這就奇怪了,怎么兩個(gè)學(xué)生的地址都改變了?

原因是淺復(fù)制只復(fù)制addr變量的引用,并沒(méi)有真正的開(kāi)辟另一塊空間,將值復(fù)制后再將引用返回給新對(duì)象。

所以,為了達(dá)到真正的復(fù)制對(duì)象,而不是純粹引用復(fù)制。需要將Address類(lèi)可復(fù)制化,并且修改clone方法,完整代碼如下:

class Address implements Cloneable {
    private String add;
    public String getAdd() {
        return add;
    }
    public void setAdd(String add) {
        this.add = add;
    }
    @Override
    public Object clone() {
        Address addr = null;
        try{
            addr = (Address)super.clone();
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return addr;
    }
}
class Student implements Cloneable{
    private int number;
    private Address addr;
    public Address getAddr() {
        return addr;
    }
    public void setAddr(Address addr) {
        this.addr = addr;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    @Override
    public Object clone() {
        Student stu = null;
        try{
            stu = (Student)super.clone();   //淺復(fù)制
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        stu.addr = (Address)addr.clone();   //深度復(fù)制
        return stu;
    }
}
public class Test {
    public static void main(String args[]) {
        Address addr = new Address();
        addr.setAdd("杭州市");
        Student stu1 = new Student();
        stu1.setNumber(123);
        stu1.setAddr(addr);
        Student stu2 = (Student)stu1.clone();
        System.out.println("學(xué)生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
        System.out.println("學(xué)生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
        addr.setAdd("西湖區(qū)");
        System.out.println("學(xué)生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
        System.out.println("學(xué)生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
    }
}

結(jié)果:

學(xué)生1:123,地址:杭州市  
學(xué)生2:123,地址:杭州市  
學(xué)生1:123,地址:西湖區(qū)  
學(xué)生2:123,地址:杭州市

到此這篇關(guān)于Java中對(duì)象的克隆詳解的文章就介紹到這了,更多相關(guān)Java對(duì)象克隆內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Online Exam在線(xiàn)考試系統(tǒng)的實(shí)現(xiàn)

    Java Online Exam在線(xiàn)考試系統(tǒng)的實(shí)現(xiàn)

    讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+springboot+vue+jsp+mysql+maven實(shí)現(xiàn)Online Exam在線(xiàn)考試系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • Java實(shí)現(xiàn)去掉字符串重復(fù)字母的方法示例

    Java實(shí)現(xiàn)去掉字符串重復(fù)字母的方法示例

    這篇文章主要介紹了Java實(shí)現(xiàn)去掉字符串重復(fù)字母的方法,涉及java針對(duì)字符串的遍歷、判斷、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下
    2017-12-12
  • Java實(shí)現(xiàn)多項(xiàng)式除法的代碼示例

    Java實(shí)現(xiàn)多項(xiàng)式除法的代碼示例

    今天小編就為大家分享一篇關(guān)于Java實(shí)現(xiàn)多項(xiàng)式除法的代碼示例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-10-10
  • SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式

    SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式

    在Web應(yīng)用程序開(kāi)發(fā)中,統(tǒng)一數(shù)據(jù)返回格式對(duì)于前后端分離項(xiàng)目尤為重要,本文就來(lái)介紹一下SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-07-07
  • HttpClient實(shí)現(xiàn)調(diào)用外部項(xiàng)目接口工具類(lèi)的示例

    HttpClient實(shí)現(xiàn)調(diào)用外部項(xiàng)目接口工具類(lèi)的示例

    下面小編就為大家?guī)?lái)一篇HttpClient實(shí)現(xiàn)調(diào)用外部項(xiàng)目接口工具類(lèi)的示例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • Java?8?中?Function?接口使用方法介紹

    Java?8?中?Function?接口使用方法介紹

    這篇文章主要介紹了Java?8中?Function接口使用方法介紹,Java8中提供了一個(gè)函數(shù)式接口Function,這個(gè)接口表示對(duì)一個(gè)參數(shù)做一些操作然后返回操作之后的值
    2022-06-06
  • Java實(shí)現(xiàn)隨機(jī)驗(yàn)證碼具體代碼

    Java實(shí)現(xiàn)隨機(jī)驗(yàn)證碼具體代碼

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)隨機(jī)驗(yàn)證碼具體代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • Java基于字符界面的簡(jiǎn)易收銀臺(tái)

    Java基于字符界面的簡(jiǎn)易收銀臺(tái)

    這篇文章主要為大家詳細(xì)介紹了Java基于字符界面的簡(jiǎn)易收銀臺(tái),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • java中Swing五種常見(jiàn)的布局方式

    java中Swing五種常見(jiàn)的布局方式

    本文通過(guò)代碼示例給大家詳細(xì)講解了java中Swing五種常見(jiàn)的布局方式,以及相關(guān)注意知識(shí)點(diǎn),有興趣的朋友參考學(xué)習(xí)下。
    2018-03-03
  • javaweb 項(xiàng)目初始配置的方法步驟

    javaweb 項(xiàng)目初始配置的方法步驟

    本文主要介紹了javaweb 項(xiàng)目初始配置的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11

最新評(píng)論