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

一文帶你認(rèn)識(shí)Java中的Object類和深淺拷貝

 更新時(shí)間:2023年04月23日 09:48:29   作者:牛牛要堅(jiān)持  
任何變成語言中,其實(shí)都有淺拷貝和深拷貝的概念,Java 中也不例外,下面這篇文章主要給大家介紹了關(guān)于Java中Object類和深淺拷貝的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

本文介紹了Object類以及Object類部分方法,toString方法,equals和hashCode方法(重寫前和重寫后的對(duì)比),getClass方法,clone方法,以及拷貝新對(duì)象時(shí)會(huì)出現(xiàn)的深淺拷貝,內(nèi)容較長(zhǎng),耗時(shí)一天,建議收藏后觀看~

一.初識(shí)Object類

Object類是Java默認(rèn)提供的一個(gè)類。而這個(gè)類是Java里所有類的頂級(jí)父類,即在繼承體系下,除了Object以外的所有類都可能有自己的父類,但Object類沒有父類,
并且所有的類同時(shí)也都默認(rèn)繼承Object類…
Object類是在Java.lang 包中的,默認(rèn)已經(jīng)導(dǎo)入了此類

有了Object類,可以使方法的形參接受任何類的對(duì)象,也可以使返回類型返回任意類的對(duì)象,可以使所有類都繼承一些Object共有的方法,故Object類是非常重要的類!!!

1.Object類接收所有子類實(shí)例

也就是說,不管是Java庫里的類還是自己定義的類,不管它們是否有自己的父類,都默認(rèn)繼承著Object類…

Object類的存在,使所有類之間有了聯(lián)系,根據(jù)向上轉(zhuǎn)型的用法:父類引用可以接收子類對(duì)象地址…

那么就可以使用Object類型的引用接受所有類的實(shí)例對(duì)象

示例:

class Student{//自定義學(xué)生類
    String name;
    Student(String name){
        this.name=name;
    }
}
class Animal{//自定義動(dòng)物類
    String name;
    Animal(String name){
        this.name=name;
    }
}
public class Text {


    public static void function(Object obj) {
        System.out.println(obj);
    }

    public static void main(String[] args) {
        function(new Animal("動(dòng)物"));
        function(new Student("學(xué)生"));
    }
}

Person和Animal類都是自定義的類,其沒有extends顯示繼承其他類,但也默認(rèn)繼承Object類,那么可以通過調(diào)用function方法形參用Object類型的引用obj接受每個(gè)對(duì)象,而此時(shí)發(fā)生向上轉(zhuǎn)型,而通過輸出方法println輸出obj,最后輸出了兩個(gè)對(duì)象的內(nèi)容!!

思考:

通過傳遞的對(duì)象不同,輸出的內(nèi)容也不同,那這種實(shí)現(xiàn)是否是多態(tài)呢?

多態(tài)即調(diào)用同一方法但不同對(duì)象會(huì)體現(xiàn)出不同的狀態(tài),這里看似是不同狀態(tài),但是多態(tài)實(shí)現(xiàn)條件沒有滿足,因?yàn)椴]有發(fā)生重寫和動(dòng)態(tài)綁定

輸出的內(nèi)容本應(yīng)是每個(gè)對(duì)象的名字,但為什么會(huì)是一堆字母串?

以demo6.Animal@1b6d3586為例 demo6.Animal即是類的全路徑(包名+類名)
@是分隔符, 而1b6d3586是一串十六進(jìn)制數(shù)的字符可以暫時(shí)理解為對(duì)象的地址

那println方法底層是怎么輸出這些的呢?

下面是println的一些源碼分析:

此方法是用Object 類型引用接受 即可以接收任意類的實(shí)例,即所有對(duì)象都能輸出

可見最關(guān)鍵的是String.valueOf方法,即是將對(duì)象內(nèi)容轉(zhuǎn)換為字符串形式

再進(jìn)入到此方法進(jìn)行分析↓

valueOf方法 返回的即是對(duì)象的字符串形式,而當(dāng)obj接受的是null時(shí),直接返回null

當(dāng)不為空時(shí),即通過toString方法獲取到對(duì)象的內(nèi)容的字符串形式返回…

重點(diǎn)來了:

toString方法在定義Person,和Animal時(shí)并沒有寫此方法,而這兩個(gè)類也沒有顯示繼承任何的類,而obj指向的對(duì)象是Person或者Animal,說明toString方法只能是Object類里面的

故這兩個(gè)類沒有重寫toString方法,也就沒有發(fā)生動(dòng)態(tài)綁定,調(diào)用和執(zhí)行的一直是Object類的toString方法,故也沒有發(fā)生多態(tài)

簡(jiǎn)單了解 在Object類里的toString方法

getClass()是獲取到Person或者Animal的類示例,.然后getName()獲取到其類所在的全路徑的以字符串形式返回

@是分隔符
hashCode()得到的是Person或者Animal實(shí)例的hash值,是一個(gè)整數(shù)(主要用于區(qū)分不同的對(duì)象,但可能會(huì)存在不同的對(duì)象hash值相同的情況(哈希沖突) )

而Integer.toHexString方法即是將獲取到的hash值轉(zhuǎn)換為十六進(jìn)制的字符串形式

以此最后得到類似于demo6.Animal@1b6d3586 這種輸出格式

注意: hashCode 和getClass()底層都是native修飾的本地方法,底層由C或者C++代碼實(shí)現(xiàn),先明白此方法的大概用途即可

2.Object類部分方法介紹

Object是所有類的父類,其內(nèi)部定義了一些成員方法,這些方法都會(huì)被子類繼承,一些通常會(huì)被子類重寫來使用,學(xué)會(huì)使用這些方法是很有必要的

以下是Object內(nèi)的成員方法簡(jiǎn)單介紹,

簡(jiǎn)單介紹下被圈起來的成員方法,而其他的方法需要在線程方面用到…

①.Object內(nèi)的toString方法

toString方法主要就是針對(duì)對(duì)象的內(nèi)容,即將對(duì)象的內(nèi)容轉(zhuǎn)換成字符串形式返回

在上面介紹到,當(dāng)想輸出某個(gè)對(duì)象內(nèi)容可以通過調(diào)用println輸出方法,println方法的形參是Object類型接受,然后會(huì)調(diào)用String.valueOf方法 將對(duì)象內(nèi)容轉(zhuǎn)換為字符串,而此方法內(nèi)會(huì)調(diào)用toString方法,最后輸出的是:類的全路徑@hashCode的十六進(jìn)制形式

但是這并不符合我們想要輸出的對(duì)象內(nèi)容,當(dāng)Person對(duì)象和Animal對(duì)象想要輸出的內(nèi)容是對(duì)象內(nèi)的成員變量name要怎么做?

此時(shí)就要重寫toString方法↓

1.可以根據(jù)Objec里的toString的方法在對(duì)應(yīng)的子類里寫同樣的權(quán)限修飾符返回值類型 方法名(需滿足重寫的要求) 然后具體方法體根據(jù)自己實(shí)現(xiàn)

public String toString(){

  //重寫的內(nèi)容...
}

2.使用IDEA快捷鍵或者右擊,選擇Generate ->toString 自動(dòng)生成重寫的toString

@Override
    public String toString() {
        return "Animal{" +   //自動(dòng)生成的內(nèi)容  ,也可以根據(jù)需求自己修改
                "name='" + name + '\'' +
                '}';
    }

當(dāng)在Person和Animal類里重寫了toString方法后,再次調(diào)用上面function方法其內(nèi)部輸出obj接受的對(duì)象內(nèi)容即能輸出每個(gè)對(duì)象的指定內(nèi)容

class Animal{
    String name;
    Animal(String name){
        this.name=name;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                '}';
    }
}
class Student {
    String name;

    Student(String name) {
        this.name = name;
    }

    @Override
    public String toString() {  //重寫內(nèi)容
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }

}
public class Text {


    public static void function(Object obj) {
        System.out.println(obj);
    }

    public static void main(String[] args) {
        function(new Animal("動(dòng)物"));
        function(new Student("學(xué)生"));
    }
 }

obj接受Person或者Animal對(duì)象,調(diào)用println方法,而內(nèi)部最后會(huì)調(diào)用Object類里的toString方法得到對(duì)象內(nèi)容的字符串形式,但是因?yàn)榘l(fā)生了子類重寫Object類的toString方法

此時(shí)調(diào)用Object類的toString方法執(zhí)行的是子類自己重寫的Object類的toString方法(多態(tài))

而Person和Animal兩個(gè)類都重寫了toString,此時(shí)則發(fā)生了多態(tài)…

最后根據(jù)自己設(shè)置的對(duì)象內(nèi)容輸出了對(duì)應(yīng)的對(duì)象…

總結(jié):

toString沒有重寫時(shí)輸出的是對(duì)象其類的全路徑@十六進(jìn)制形式的hashCode值
toString重寫時(shí)輸出的是自己重寫的指定對(duì)象內(nèi)容 (更常用)

②.Object內(nèi)的equals和hashCode方法

equals是比較當(dāng)前對(duì)象和指定對(duì)象是否相等,hashCode是獲取當(dāng)前對(duì)象的哈希值
二者通常是成對(duì)出現(xiàn)的!

1.Object內(nèi)的equals方法和hashCode方法

Object內(nèi)的equals方法:

this引用表示當(dāng)前對(duì)象的引用存放的當(dāng)前對(duì)象的地址,而obj引用接受的是要比較的對(duì)象的地址…

故Object內(nèi)的equals方法比較兩個(gè)對(duì)象時(shí),比較的是兩個(gè)對(duì)象的地址,而對(duì)象不同則對(duì)象地址一定是不相等,即絕大部分返回的值都是false

而我們大部分情況下判斷對(duì)象是否相等并不是看對(duì)象地址,而是看對(duì)象的內(nèi)容是否相等(成員變量?jī)?nèi)的值),

要實(shí)現(xiàn)這種要求,就要重寫equals方法! (根據(jù)內(nèi)容比較對(duì)象是否相等)

Object內(nèi)的hashCode方法:

此方法是native修飾的本地方法,無法直接看到源碼.但大致就是通過對(duì)象地址根據(jù)對(duì)應(yīng)規(guī)則生成一個(gè)整數(shù)即哈希值

簡(jiǎn)單了解hashCode的用途:

hashCode方法在作用在哈希桶處體現(xiàn),所謂哈希桶就是一個(gè)鏈表數(shù)組,用來動(dòng)態(tài)的搜索數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu).,主要作用用來搜索 故哈希桶內(nèi)相同的對(duì)象只會(huì)存放一份!

當(dāng)你要存放對(duì)象在哈希桶內(nèi)時(shí),即是對(duì)應(yīng)到數(shù)組某個(gè)下標(biāo)的空間內(nèi)存放該對(duì)象,而對(duì)象怎么得到一個(gè)整形下標(biāo),即是通過了hashCode方法生成的一個(gè)整數(shù),最后通過該整數(shù)與數(shù)組長(zhǎng)度取余映射到數(shù)組的對(duì)應(yīng)下標(biāo)!

但是hashCode生成的整數(shù)跟對(duì)象地址有關(guān),那么不同對(duì)象會(huì)生成不同的整數(shù),但是實(shí)際情況判斷一個(gè)對(duì)象是否相同不是看對(duì)象地址,而是看對(duì)象內(nèi)容,當(dāng)內(nèi)容完全一樣,而hashCode又不同,會(huì)發(fā)生在一個(gè)哈希桶內(nèi)存放著兩個(gè)內(nèi)容一樣的對(duì)象,

或者當(dāng)又new了一個(gè)新對(duì)象其內(nèi)容和之前對(duì)象完全相同,可是通過調(diào)用hashCode方法根據(jù)對(duì)象地址生成的哈希值不同映射到不同數(shù)組下標(biāo),而在此下標(biāo)對(duì)應(yīng)的空間內(nèi)又沒有找到和之前對(duì)象內(nèi)容完全相同的,則會(huì)出現(xiàn)在哈希桶內(nèi)查找對(duì)象時(shí),找不到內(nèi)容相同的對(duì)象但是哈希桶內(nèi)又存在這樣的對(duì)象,則達(dá)不到我們所實(shí)現(xiàn)的要求…

而要實(shí)現(xiàn)映射的數(shù)組下標(biāo)即要使獲得的hashCode相同,那么就要重寫hashCode方法(根據(jù)對(duì)象內(nèi)容生成哈希值)

===============================================================

2.重寫equals方法和hashCode方法

上面介紹在Object內(nèi)的equals方法和hashCode方法不滿足實(shí)際需求,此時(shí)需要在子類里根據(jù)實(shí)際需求重寫這兩個(gè)方法,重寫之后即會(huì)實(shí)現(xiàn)動(dòng)態(tài)綁定 調(diào)用我們重寫的方法

重寫equals方法和hashCode方法可以根據(jù)其Object內(nèi)的方法在子類里寫滿足重寫要求的方法,方法體根據(jù)自己需求而改, 要考慮實(shí)際情況重寫,hashCode也要自己定一套根據(jù)不同內(nèi)容生成對(duì)應(yīng)的整數(shù)的哈希方法

而在IDEA里提供了快捷生成的重寫方法,右擊 Generate 點(diǎn)擊equals and hashCode 一路Next即可 自動(dòng)根據(jù)對(duì)象內(nèi)容生成 equals方法和hashCode方法

@Override
    public boolean equals(Object o) {
        if (this == o) return true;// 對(duì)象地址相等返回true
        if (o == null || getClass() != o.getClass()) 
        return false; 要比較的對(duì)象為null或者 兩個(gè)對(duì)象是不同的類實(shí)例 返回false
        
        Animal animal = (Animal) o;
        return Objects.equals(name, animal.name);
        //根據(jù)Objects類的equals方法 傳指定內(nèi)容比較是否相等返回結(jié)果
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);//根據(jù)Objects類的hash方法傳指定參數(shù) 即根據(jù)其參數(shù)內(nèi)容生成hashCode
    }

自動(dòng)生成的equals方法和hashCode方法底層都調(diào)用了Objects類的方法,而Objects類即是Object類的api,里面提供的都是靜態(tài)方法,主要用于當(dāng)重寫Object類時(shí),根據(jù)子類對(duì)象的內(nèi)容調(diào)用Objects對(duì)應(yīng)的方法,分化成子類內(nèi)容之間進(jìn)行操作

equals(name, animal.name);

接受當(dāng)前對(duì)象的name和指定比較對(duì)象的name, 而此時(shí)的a.equals(b) 轉(zhuǎn)換為了name之間的比較,而name是String類型,其類已經(jīng)封裝好了對(duì)equals的重寫(即判斷每個(gè)字符是否相等),通過此方法得出對(duì)應(yīng)的內(nèi)容是否相等,當(dāng)有多個(gè)參數(shù)比較則會(huì)依次調(diào)用多個(gè)此方法

===============================================================

Objects.hash(name);

其內(nèi)部的hash方法形參是Object…values

而這個(gè)語法叫做可變參數(shù):

在 Java 5 中提供了變長(zhǎng)參數(shù),允許在調(diào)用方法時(shí)傳入不定長(zhǎng)度的參數(shù)。變長(zhǎng)參數(shù)是 Java 的一個(gè)語法糖,本質(zhì)上還是基于數(shù)組的實(shí)現(xiàn):

在定義方法時(shí),在最后一個(gè)形參后加上三點(diǎn) …,就表示該形參可以接受多個(gè)參數(shù)值,多個(gè)參數(shù)值被當(dāng)成數(shù)組傳入。上述定義有幾個(gè)要點(diǎn)需要注意:

可變參數(shù)只能作為函數(shù)的最后一個(gè)參數(shù),但其前面可以有也可以沒有任何其他參數(shù)

由于可變參數(shù)必須是最后一個(gè)參數(shù),所以一個(gè)函數(shù)最多只能有一個(gè)可變參數(shù)

Java的可變參數(shù),會(huì)被編譯器轉(zhuǎn)型為一個(gè)數(shù)組

變長(zhǎng)參數(shù)在編譯為字節(jié)碼后,在方法簽名中就是以數(shù)組形態(tài)出現(xiàn)的。這兩個(gè)方法的簽名是一致的,不能作為方法的重載。如果同時(shí)出現(xiàn),是不能編譯通過的??勺儏?shù)可以兼容數(shù)組,反之則不成立

使用了可變參數(shù)說明hash()實(shí)參可以有多個(gè),根據(jù)實(shí)際情況而定,最后都會(huì)被接受放在Object數(shù)組里,此時(shí)再調(diào)用其內(nèi)部封裝的hashCode , 按相應(yīng)的規(guī)則,調(diào)用數(shù)組每一個(gè)元素的hashCode方法,因?yàn)槊恳粋€(gè)元素都是指定對(duì)象的內(nèi)容 可能是字符串類型或者是Integer類型等, 其內(nèi)部都封裝了hashCode方法,有自己根據(jù)自身內(nèi)容生成哈希值的方法,最后合并得到一個(gè)哈希值返回給Objects.hash方法調(diào)用方得到一個(gè)根據(jù)對(duì)象指定內(nèi)容生成的哈希值

故使用編譯器自動(dòng)生成的重寫equals和hashCode方法,會(huì)編寫好相應(yīng)的方法體,在我們使用的時(shí)候,根據(jù)需求對(duì)對(duì)象相應(yīng)內(nèi)容要比較的參數(shù)傳上去,根據(jù)指定內(nèi)容或者全部?jī)?nèi)容以此判斷對(duì)象是否相等以及生成相應(yīng)的hash值!

為什么equals和hashCode需要一起重寫?

因?yàn)楫?dāng)只重寫了equals表示會(huì)根據(jù)相應(yīng)內(nèi)容和指定對(duì)象進(jìn)行比較是否相等,此時(shí)沒有重寫hashCode則在哈希桶里仍然會(huì)出現(xiàn)指定內(nèi)容相同的對(duì)象出現(xiàn)多份或者有指定內(nèi)容相等的對(duì)象但是沒有找到此對(duì)象的情況

而只重寫hashCode是沒有意義的…哈希桶里即便出現(xiàn)了對(duì)象內(nèi)容完全一樣的,也會(huì)因?yàn)閷?duì)象地址不同而判定為兩個(gè)不同的對(duì)象…
所以二者一定要有關(guān)聯(lián),且要一起重寫…

equals和hashCode的關(guān)系

equals判斷兩個(gè)對(duì)象相等其hashCode一定相等嘛?

equals判斷兩個(gè)對(duì)象完全相等,要么地址相等要么指定內(nèi)容相等,對(duì)應(yīng)的如果重寫了equals也就會(huì)根據(jù)內(nèi)容重寫hashCode 那么hashCode一定會(huì)相等

hashCode相等 equals判斷兩個(gè)對(duì)象一定會(huì)相等嘛?

hashCode相等只能說明在指定的獲取哈希的函數(shù)里兩個(gè)對(duì)象的指定內(nèi)容或者地址最后生成的哈希值是相同的,但是也有可能內(nèi)容地址不同卻出現(xiàn)相同的哈希值(哈希沖突),但是此時(shí)equals并不會(huì)相同!

總結(jié):

equals相同,hashCode一定相同, hashCode相同 equals不一定相同,

equals不同,hashCode可能相同,hashCode不同則equals一定不相同

③.Object類的getClass方法

getClass方法是Java中獲取類實(shí)例的一種方法,而類實(shí)例主要用于反射中使用

簡(jiǎn)單了解下反射機(jī)制:

Java文件被編譯后,生成了.class文件,JVM此時(shí)就要去解讀.class文件
,被編譯后的Java文件.class也被JVM解析為一個(gè)對(duì)象,這個(gè)對(duì)象就是 java.lang.Class .
這樣當(dāng)程序在運(yùn)行時(shí),每個(gè)java文件就最終變成了Class類對(duì)象的一個(gè)實(shí)例。我們通過Java的反射機(jī)制應(yīng)用到這個(gè)實(shí)例,就可以去獲得甚至去添加改變這個(gè)類的屬性和動(dòng)作,使得這個(gè)類成為一個(gè)動(dòng)態(tài)的類

而一個(gè)類文件 只有一個(gè)類實(shí)例,一個(gè)類實(shí)例化多個(gè)對(duì)象,通過這多個(gè)對(duì)象調(diào)用getClass獲得的類實(shí)例都是同一個(gè)

在使用快捷命令重寫equals方法生成的代碼中使用了getClass方法,

即是獲得當(dāng)前對(duì)象的類實(shí)例,

和Object類型的o引用接受的對(duì)象調(diào)用getClass獲得的類實(shí)例(即o指向的對(duì)象的類實(shí)例)判斷是否相等,如果是同一個(gè)類實(shí)例化的對(duì)象則獲取的類實(shí)例是同一個(gè),如果是不同 的類的實(shí)例對(duì)象 則會(huì)返回false…此處即是為了判斷兩個(gè)對(duì)象是否是同一個(gè)類實(shí)例而來

也可以使用 if ( ! o instanceof Person) return false;判斷 o指向的對(duì)象是否是Person的實(shí)例,從而判斷兩個(gè)對(duì)象是否是同一個(gè)類實(shí)例而來

④.Object類的clone方法

Object類里的clone方法是native修飾的本地方法底層由C/C++實(shí)現(xiàn)是直接供對(duì)象調(diào)用的方法,當(dāng)對(duì)象調(diào)用clone方法會(huì)在堆區(qū)創(chuàng)建一份和原對(duì)象一模一樣的對(duì)象返回(即屬性和行為都一樣)

雖然clone方法可以直接調(diào)用,但并不是字面意思上直接調(diào)用

class Person{
    String name;
    int age;
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
}

當(dāng)有了上面這個(gè)類,我們是否可以在main方法中直接創(chuàng)建一份Person對(duì)象,再直接調(diào)用clone方法再用一個(gè)Person引用接受克隆的新對(duì)象呢?

此時(shí)會(huì)編譯報(bào)錯(cuò), 在Java中clone被看成一種公共特性,即提供了一個(gè)Cloneable接口,其是一個(gè)空接口,表示一個(gè)標(biāo)準(zhǔn)規(guī)范,只有實(shí)現(xiàn)了此接口的類才能調(diào)用clone方法進(jìn)行克隆,如果沒有實(shí)現(xiàn),則會(huì)報(bào)CloneNotSupportedException異常

class Person implements Cloneable{
    String name;
    int age;
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
}

當(dāng)Person實(shí)現(xiàn)了Cloneable具備了克隆特性 是否可以克隆了呢?

注意! clone方法在Object類是protected修飾的!!!

當(dāng)被protected修飾的成員,在當(dāng)前包里可以直接被訪問,但是在不同包里,只能在其對(duì)應(yīng)的子類內(nèi)被訪問!

即調(diào)用的是Person對(duì)象繼承的Object類的clone方法,Person是Object的子類,那么就要在Person類內(nèi)調(diào)用clone方法,Test類雖然也是Object的子類但是調(diào)用的是Person,所以在Test類里是不能調(diào)用到Person類對(duì)象的clone方法

為了實(shí)現(xiàn)接口方法統(tǒng)一使在Test類里能夠調(diào)用到Person的clone方法,這里也要在Person類里重寫clone方法,但這個(gè)重寫只是給類外提供調(diào)用clone的方法,并沒有重寫clone本身的方法

通過 在Person 里重寫 clone方法 方法體 是調(diào)用其父類Object類的clone方法,但是會(huì)拋出一個(gè)編譯時(shí)異常,此異常為了提醒程序員需要處理此異常,即必須try-catch處理或者throws 聲明異常拋給上層調(diào)用方

使用throws層層聲明異常后↓

到這里最后一步,能夠調(diào)用Person的clone方法 克隆一份一樣的對(duì)象,但是clone方法的返回類型是Object類型的,不能直接用Person接受,向下轉(zhuǎn)型需要這里強(qiáng)轉(zhuǎn)

最終的寫法↓

class Person implements Cloneable{//Person實(shí)現(xiàn)Cloneable 支持克隆
    String name;
    int age;
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Text {//Cloneable是一個(gè)接口 空接口 是一個(gè)標(biāo)記接口 標(biāo)準(zhǔn)規(guī)范 唯一作用 表示當(dāng)前類實(shí)例化的對(duì)象是可以被克隆的,如果沒有實(shí)現(xiàn)這個(gè)接口就不能被克隆
    //實(shí)現(xiàn)cloneable接口 當(dāng)前對(duì)象可以克隆 但是需要重寫object類的clone方法,但是這個(gè)重寫只能為了調(diào)用父類clone方法!!! 沒有實(shí)現(xiàn)這兩種會(huì)報(bào)編譯時(shí)異常!

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person("張三", 18);
        Person clonePerson = (Person) person.clone();
        
    }

}

為什么clone返回類型是Object類型?

一個(gè)方法只有一個(gè)返回類型,而實(shí)現(xiàn)cloneable接口的對(duì)象一般都支持克隆,但是既然可以克隆很多對(duì)象,那么返回類型是不確定具體返回的對(duì)象類是哪個(gè),而根據(jù)Object類的特性,其是所有類的父類,那么就可以達(dá)成通用, 借助向上轉(zhuǎn)型返回對(duì)象,在外層由程序員自己進(jìn)行向下轉(zhuǎn)型 從而實(shí)現(xiàn)對(duì)應(yīng)的類型接受對(duì)應(yīng)的克隆對(duì)象

二.認(rèn)識(shí)深拷貝和淺拷貝

在認(rèn)識(shí)Object的clone方法后,我們能直接拷貝一份和對(duì)象一模一樣的新對(duì)象出來,但是在拷貝對(duì)象時(shí),因?yàn)橐米兞看娣诺氖菍?duì)象地址,故在拷貝時(shí)還要區(qū)分深拷貝和淺拷貝!

1.什么是深淺拷貝?

淺拷貝即是當(dāng)修改拷貝的對(duì)象內(nèi)容時(shí), 原被拷貝對(duì)象的內(nèi)容也會(huì)隨之被修改,即兩個(gè)對(duì)象共用同一塊內(nèi)容,看似拷貝了一份全新的對(duì)象,但是這個(gè)對(duì)象的成員和原對(duì)象的成員仍然共用同一份空間
深拷貝即當(dāng)修改拷貝對(duì)象內(nèi)容時(shí),原拷貝對(duì)象內(nèi)容不會(huì)改變,即兩個(gè)對(duì)象的所有內(nèi)容也是獨(dú)立被拷貝的!

class Money implements Cloneable{
    public double m=3.14;

    @Override
    public String toString() {
        return "Money{" +
                "m=" + m +
                '}';
    }
}
public class Student implements Cloneable {
    Money money=new Money();
    public String name;
    public int age=18;
    public Student(String name){
        this.name=name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
    return super.clone();
    }

    @Override
     public String toString() {
        return "Student{" +
                "money=" + money +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public static void main(String[] args) throws CloneNotSupportedException{
        Student student=new Student("666");
        Student student1=(Student) student.clone();
        System.out.println(student);
        System.out.println(student1);
        student1.age=20;// 基本數(shù)據(jù)類型存放的是原對(duì)象的數(shù)據(jù) 此時(shí)修改基本數(shù)據(jù)類型, 原對(duì)象并不會(huì)受到影響
        student1.name="000";//name雖然指向的是同一個(gè)對(duì)象 但是后面直接是實(shí)例化另一個(gè)對(duì)象給克隆的引用變量本質(zhì)上
        // 克隆對(duì)象的name一開始指向的和原對(duì)象的name指向的字符串對(duì)象一樣
        student1.money.m=100.0;   //會(huì)克隆一份對(duì)象 里面成員變量值和方法是和原對(duì)象一樣的
        //引用數(shù)據(jù)類型存放的是原對(duì)象的對(duì)象地址 這就造成 克隆后對(duì)象內(nèi)的引用變量指向的是同一塊對(duì)象地址
        //此時(shí)通過引用變量修改對(duì)象內(nèi)容 原來的和克隆的占用的是同一份 淺拷貝!!! money指向的同一塊對(duì)象里面的m成員 共用一塊空間
        System.out.println("更改后===========");
        System.out.println(student);
        System.out.println(student1);
    }

}

通過上面代碼運(yùn)行后 發(fā)現(xiàn) 只對(duì)克隆的對(duì)象內(nèi)容進(jìn)行修改后,但有些內(nèi)容原對(duì)象也跟著改變,這即是發(fā)生淺拷貝, 拷貝的程度淺,對(duì)象拷貝了一份但內(nèi)容卻沒有真正拷貝.

age是基本數(shù)據(jù)類型,拷貝的對(duì)象內(nèi)也存有一份age數(shù)據(jù),修改這個(gè)age不會(huì)影響原對(duì)象的age值,name是引用類型數(shù)據(jù) 指向字符串對(duì)象, 而修改拷貝對(duì)象內(nèi)的字符串對(duì)象本質(zhì)是創(chuàng)建了一個(gè)新字符串,故也沒有影響原對(duì)象,

但是Menoy引用指向的一份對(duì)象,經(jīng)過拷貝后 新對(duì)象的Menoy引用同樣存放著原Menoy對(duì)象的地址,此時(shí)通過新對(duì)象修改Menoy指向的對(duì)象內(nèi)的值,原對(duì)象內(nèi)的Menoy對(duì)象內(nèi)的值也會(huì)發(fā)生改變

此時(shí)的拷貝形式正是淺拷貝,對(duì)象并沒有完全被拷貝,而如何實(shí)現(xiàn)深拷貝呢?

2.實(shí)現(xiàn)深拷貝

淺拷貝即拷貝了新對(duì)象,但是新對(duì)象的內(nèi)容可能和原對(duì)象共用一塊空間,故要實(shí)現(xiàn)深拷貝,要在原來拷貝的基礎(chǔ)上,對(duì)可能共用一塊空間的內(nèi)容進(jìn)行再拷貝一份

在上面代碼基礎(chǔ)上,使Menoy引用指向不同對(duì)象則要對(duì)Menoy對(duì)象單獨(dú)進(jìn)行克隆

class Money implements Cloneable{
    public double m=3.14;


    @Override
    public String toString() {
        return "Money{" +
                "m=" + m +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();   //這里用于給 money克隆一份
    }
}
public class Student implements Cloneable {
    Money money=new Money();
    public String name;
    public int age=18;
    public Student(String name){
        this.name=name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        student.money=(Money) this.money.clone(); //調(diào)用 money的clone方法為其克隆出一份 
        return student; //自動(dòng)向上轉(zhuǎn)型
    }

    @Override
    public String toString() {
        return "Student{" +
                "money=" + money +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

對(duì)象內(nèi)主要是Menoy對(duì)象是同一個(gè)空間,那么在克隆對(duì)象時(shí),先克隆出新Student對(duì)象,再根據(jù)指定Menoy對(duì)象克隆出新的Menoy對(duì)象(Menoy也要支持clone)給克隆對(duì)象的Menoy引用,最后返回克隆的新對(duì)象, 最后實(shí)現(xiàn)了深拷貝,兩個(gè)對(duì)象的內(nèi)容是獨(dú)立不互不影響的

注意: 除了clone能拷貝對(duì)象以外還有其他方法能對(duì)對(duì)象進(jìn)行拷貝,如Arrays.copyOf方法能夠?qū)χ付〝?shù)組對(duì)象進(jìn)行拷貝,對(duì)數(shù)組進(jìn)行拷貝 也會(huì)出現(xiàn)深淺拷貝的現(xiàn)象,拷貝的新數(shù)組每個(gè)引用可能指向的原數(shù)組的每個(gè)引用指向的對(duì)象…

故在拷貝時(shí)要注意需要的是深拷貝還是淺拷貝…

三.Object類和深淺拷貝總結(jié)

本篇博客介紹了Object類 以及其內(nèi)部的一些方法:toString(重寫前:獲取其類的全路徑@對(duì)象地址,重寫后將對(duì)象內(nèi)容轉(zhuǎn)換為字符串形式返回),
equals和hashCode(重寫前:判斷對(duì)象的地址是否相等和根據(jù)對(duì)象地址生成哈希值,重寫后:判斷對(duì)象指定內(nèi)容是否相等和根據(jù)指定內(nèi)容獲取對(duì)象生成的哈希值),
getClass (獲取類實(shí)例),clone(克隆對(duì)象需要注意權(quán)限和異常)
以及跟克隆相關(guān)的深淺拷貝(對(duì)新對(duì)象內(nèi)容進(jìn)行修改是否會(huì)影響原對(duì)象的內(nèi)容)

到此這篇關(guān)于Java中的Object類和深淺拷貝的文章就介紹到這了,更多相關(guān)Java Object類和深淺拷貝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • windows下 jdk1.7安裝教程圖解

    windows下 jdk1.7安裝教程圖解

    java編程的初學(xué)者在開始編碼前都會(huì)遇到一個(gè)難題,那就是jdk1.7環(huán)境變量配置怎么操作,怎么安裝,針對(duì)這個(gè)難題,小編特地為大家整理相關(guān)教程,不了解的朋友可以前往查看使用
    2018-05-05
  • SWT(JFace)體驗(yàn)之ApplicationWindow

    SWT(JFace)體驗(yàn)之ApplicationWindow

    SWT(JFace)體驗(yàn)之ApplicationWindow
    2009-06-06
  • idea同時(shí)編輯多行問題-win&mac都支持

    idea同時(shí)編輯多行問題-win&mac都支持

    這篇文章主要介紹了idea同時(shí)編輯多行問題-win&mac都支持,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • SpringBoot使用JdbcTemplate訪問操作數(shù)據(jù)庫基本用法

    SpringBoot使用JdbcTemplate訪問操作數(shù)據(jù)庫基本用法

    這篇文章主要介紹了SpringBoot使用JdbcTemplate訪問操作數(shù)據(jù)庫基本用法,Spring對(duì)數(shù)據(jù)庫的操作在jdbc上s面做了深層次的封裝,使用spring的注入功能,可以把DataSource注冊(cè)到JdbcTemplate之中。下文詳細(xì)內(nèi)容需要的小伙伴可以參考一下
    2022-02-02
  • 詳解Mybatis中的CRUD

    詳解Mybatis中的CRUD

    這篇文章主要介紹了Mybatis中的CRUD的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Spring Boot報(bào)錯(cuò):No session repository could be auto-configured, check your configuration的解決方法

    Spring Boot報(bào)錯(cuò):No session repository could be auto-configured

    這篇文章主要給大家介紹了關(guān)于Spring Boot報(bào)錯(cuò):No session repository could be auto-configured, check your configuration的解決方法,文中給出了詳細(xì)的解決方法,對(duì)遇到這個(gè)問題的朋友們具有一定參考價(jià)值,需要的朋友下面來一起看看吧。
    2017-07-07
  • MybatisPlus+Postgresql整合的幾個(gè)坑及解決

    MybatisPlus+Postgresql整合的幾個(gè)坑及解決

    這篇文章主要介紹了MybatisPlus+Postgresql整合的幾個(gè)坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Spring深入了解常用配置應(yīng)用

    Spring深入了解常用配置應(yīng)用

    這篇文章主要給大家介紹了關(guān)于Spring的常用配置,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用springboot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Spring實(shí)現(xiàn)動(dòng)態(tài)切換多數(shù)據(jù)源的解決方案

    Spring實(shí)現(xiàn)動(dòng)態(tài)切換多數(shù)據(jù)源的解決方案

    這篇文章主要給大家介紹了Spring實(shí)現(xiàn)動(dòng)態(tài)切換多數(shù)據(jù)源的解決方案,文中給出了詳細(xì)的介紹和示例代碼,相信對(duì)大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,有需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。
    2017-01-01
  • 淺談Java注解和動(dòng)態(tài)代理

    淺談Java注解和動(dòng)態(tài)代理

    這篇文章主要介紹了Java中有關(guān)注解和動(dòng)態(tài)代理的一些知識(shí),涉及了Annotation、數(shù)據(jù)類型等相關(guān)內(nèi)容,需要的朋友可以參考下。
    2017-09-09

最新評(píng)論