Java中Object用法詳解
一. Object簡(jiǎn)介
1. 簡(jiǎn)介
在了解Object中的常用方法之前,我們先來(lái)看看Object類(lèi)的源碼,如下所示:
/** * Class {@code Object} is the root of the class hierarchy. * Every class has {@code Object} as a superclass. All objects, * including arrays, implement the methods of this class. * @author unascribed * @see java.lang.Class * @since JDK1.0 */ public class Object { ......
從Object類(lèi)的源碼注釋可以知道,Object類(lèi)是Java中所有類(lèi)的父類(lèi),相當(dāng)于是Java中的”萬(wàn)類(lèi)之王“,處于最頂層。 所以在Java中,所有的類(lèi)默認(rèn)都繼承自O(shè)bject類(lèi)。同時(shí)Java中的所有類(lèi)對(duì)象,包括數(shù)組,也都要實(shí)現(xiàn)這個(gè)類(lèi)中的方法。
所以,Object是Java中所有類(lèi)的父類(lèi)、超類(lèi)、基類(lèi),位于繼承樹(shù)的最頂層??梢哉f(shuō),任何一個(gè)沒(méi)有顯式地繼承別的父類(lèi)的類(lèi),都會(huì)直接繼承Object,否則就是間接地繼承Object,并且任何一個(gè)類(lèi)也都會(huì)享有Object提供的方法。又因?yàn)镺bject是所有類(lèi)的父類(lèi),所以基于多態(tài)的特性,該類(lèi)可以用來(lái)代表任何一個(gè)類(lèi),允許把任何類(lèi)型的對(duì)象賦給 Object類(lèi)型的變量,也可以作為方法的參數(shù)、方法的返回值。
二. 常用方法
在Object類(lèi)中,自帶了幾個(gè)常用的方法,這幾個(gè)方法任意的子類(lèi)都會(huì)繼承,如下圖所示:
根據(jù)上圖,小編把Object類(lèi)中的常用方法歸納為這么幾種:
構(gòu)造方法;
hashCode()和equals()方法用來(lái)判斷對(duì)象是否相同;
wait()、wait(long)、wait(long,int)、notify()、notifyAll();
toString()和getClass();
clone();
finalize()
接下來(lái)小編就給各位介紹Object類(lèi)中的幾個(gè)常用方法,分別說(shuō)一下這些方法的功能作用。
1. clone()方法
1.1 clone方法作用
Object中有兩個(gè)protected修飾的方法,其中一個(gè)就是clone()方法,并且該方法還是一個(gè)native方法。clone()方法用于創(chuàng)建復(fù)制出當(dāng)前類(lèi)對(duì)象的一個(gè)副本,得到一個(gè)復(fù)制對(duì)象。 所謂的復(fù)制對(duì)象,首先會(huì)分配一個(gè)和源對(duì)象(調(diào)用clone方法的對(duì)象)同樣大小的內(nèi)存空間,在這個(gè)內(nèi)存空間中會(huì)創(chuàng)建出一個(gè)新對(duì)象;然后再使用源對(duì)象中對(duì)應(yīng)的各個(gè)成員,填充新對(duì)象的成員,填充完成之后,clone方法會(huì)創(chuàng)建返回一個(gè)新的相同對(duì)象供外部引用。
1.2 clone源碼分析
我們?cè)倏纯碿lone()方法源碼上的注釋,如下圖所示:
從這段注釋中,我們可以了解到:
以x為藍(lán)本創(chuàng)建出的副本,與x對(duì)象并不相同,這保證了克隆出的對(duì)象擁有單獨(dú)的內(nèi)存空間;
源對(duì)象和克隆的新對(duì)象字節(jié)碼相同,它們具有相同的類(lèi)類(lèi)型,但這并不是強(qiáng)制性的;
源對(duì)象和克隆的新對(duì)象利用equals()方法比較時(shí)是相同的,但這也不是強(qiáng)制性的。
1.3 Java的淺克隆與深克隆
因?yàn)槊總€(gè)類(lèi)的直接或間接父類(lèi)都是Object,因此它們都含有clone()方法,但因該方法是protected修飾的,所以我們不能在類(lèi)外訪問(wèn)該方法。但如果我們要對(duì)一個(gè)對(duì)象進(jìn)行復(fù)制,可以對(duì)clone方法進(jìn)行復(fù)寫(xiě),而Java中提供了兩種不同的克隆方式,淺克隆(ShallowClone) 和深克隆(DeepClone)。
1.3.1 淺克隆
在淺克隆中,如果源對(duì)象的成員變量是值類(lèi)型,則復(fù)制一份給克隆對(duì)象;如果源對(duì)象的成員變量是引用類(lèi)型,則將引用對(duì)象的地址復(fù)制一份給克隆對(duì)象,也就是說(shuō)源對(duì)象和克隆對(duì)象的成員變量指向相同的內(nèi)存地址。
簡(jiǎn)單說(shuō),在淺克隆中,當(dāng)對(duì)象被復(fù)制時(shí)只復(fù)制它本身和其中包含的值類(lèi)型的成員變量,而引用類(lèi)型的成員對(duì)象并沒(méi)有復(fù)制。我們可以用下圖對(duì)淺克隆進(jìn)行展示:
在Java語(yǔ)言中,通過(guò)實(shí)現(xiàn)Cloneable接口,默認(rèn)覆蓋Object類(lèi)的clone()方法就可以實(shí)現(xiàn)淺克隆。
1.3.2 深克隆
在深克隆中,無(wú)論源對(duì)象的成員變量是值類(lèi)型還是引用類(lèi)型,都將復(fù)制一份給克隆對(duì)象,即深克隆將源對(duì)象的所有引用對(duì)象也復(fù)制一份給克隆對(duì)象。
簡(jiǎn)單來(lái)說(shuō),在深克隆中,除了對(duì)象本身被復(fù)制外,對(duì)象中包含的所有成員變量也將復(fù)制。我們可以用下圖對(duì)深克隆進(jìn)行展示:
在Java語(yǔ)言中,如果需要實(shí)現(xiàn)深克隆,可以通過(guò)實(shí)現(xiàn)Cloneable接口,自定義覆蓋Object類(lèi)的clone()方法實(shí)現(xiàn),也可以通過(guò)序列化(Serialization)等方式來(lái)實(shí)現(xiàn)。 如果引用類(lèi)型里面還包含很多引用類(lèi)型,或者內(nèi)層引用類(lèi)型的類(lèi)里面又包含引用類(lèi)型,使用clone方法就會(huì)很麻煩。這時(shí)我們可以用序列化的方式來(lái)實(shí)現(xiàn)對(duì)象的深克隆。
2. hashCode()方法
2.1 簡(jiǎn)介
hashCode()是Object中的一個(gè)native方法,也是所有類(lèi)都擁有的一個(gè)方法,主要是返回每個(gè)對(duì)象十進(jìn)制的hash值。hash值是由hash算法根據(jù)對(duì)象的地址、對(duì)象中的字符串、數(shù)字等計(jì)算出來(lái)的。一般情況下,相同的對(duì)象應(yīng)會(huì)返回相同的哈希嗎值,不同的對(duì)象會(huì)返回不同的哈希碼值。
2.2 hash值
哈希值是根據(jù)地址值換算出來(lái)的一個(gè)值,并不是實(shí)際的地址值,常用于哈希表中,如HashMap、HashTable、HashSet。關(guān)于哈希值,不同的JDK算法其實(shí)是不一樣的:
- Java 6、7 中會(huì)默認(rèn)返回一個(gè)隨機(jī)數(shù);
- Java 8 中默認(rèn)通過(guò)和當(dāng)前線程有關(guān)的一個(gè)隨機(jī)數(shù) + 三個(gè)確定值,運(yùn)用Marsaglia’s xorshift scheme的隨機(jī)數(shù)算法得到的一個(gè)隨機(jī)數(shù)。
2.3 案例
Dog dog01=new Dog("喬治01"); Dog dog02=new Dog("喬治02"); //兩個(gè)對(duì)象的hash值是不同的 System.out.println("dog01的hash值 "+dog01.hashCode()); System.out.println("dog02的hash值 "+dog02.hashCode());
以上兩個(gè)對(duì)象的hash值是不同的,表示這是不同的兩個(gè)對(duì)象。
3. equals(obj)方法
3.1 equals簡(jiǎn)介
Object中的equals方法用于判斷this對(duì)象和obj本身的值是否相等,即用來(lái)判斷調(diào)用equals方法的對(duì)象和形參obj所引用的對(duì)象是否是同一對(duì)象。 所謂同一對(duì)象,就是指兩個(gè)對(duì)象是否指向了內(nèi)存中的同一塊存儲(chǔ)單元地址。如果this和obj指向的是同一塊內(nèi)存單元地址,則返回true;如果this和obj指向的不是同一塊內(nèi)存單元地址,則返回false。如果沒(méi)有指向同一內(nèi)存單元,即便是內(nèi)容完全相等,也會(huì)返回false。
Object類(lèi)的equals方法,其作用是比較兩個(gè)對(duì)象是否相同,默認(rèn)比較的是內(nèi)存地址,其底層是通過(guò)==實(shí)現(xiàn)的。如果我們不想比較內(nèi)存地址,那么就需要重寫(xiě)equals方法。默認(rèn)的實(shí)現(xiàn)源碼如下:
public boolean equals(Object obj) { return (this == obj); }
我們知道,Java中還有一個(gè)==運(yùn)算符,也可以對(duì)兩個(gè)對(duì)象進(jìn)行比較。如果是基本數(shù)據(jù)類(lèi)型,==比較的是它們的值是否相同;如果是引用數(shù)據(jù)類(lèi)型,比較的是它們的內(nèi)存地址是否相同。而equals()方法則是比較兩個(gè)對(duì)象的內(nèi)容是否相等。
3.2 使用原則
我們?cè)谑褂胑quals()方法時(shí),需注意下面這些原則:
(1).equals()只能處理引用類(lèi)型變量;
(2).一般情況下,equals()方法比較的是兩個(gè)引用類(lèi)型變量的地址值是否相等;
(3).但是String類(lèi)、基本類(lèi)型包裝類(lèi)、File類(lèi)、Date類(lèi)等,都重寫(xiě)了Object類(lèi)的equals()方法,比較是兩個(gè)對(duì)象的"具體內(nèi)容"是否相同。
3.3 基本特性
另外Java語(yǔ)言規(guī)范也要求equals方法具有下面的特性:
自反性 : 對(duì)于任何非空引用x,x.equals(x)應(yīng)該返回true;
對(duì)稱性:對(duì)于任何引用x和y,當(dāng)且僅當(dāng)y.equals(x)返回true,x.equals(y)也應(yīng)該返回true;
傳遞性:對(duì)于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也應(yīng)該返回true;
一致性 : 如果x和y引用的對(duì)象沒(méi)有發(fā)生變化,反復(fù)調(diào)用x.equals(y)應(yīng)該返回同樣的結(jié)果;
對(duì)于任何非空引用x,x.equals(null)應(yīng)該返回false。
3.4 案例
/** * @author 一一哥Sun * 千鋒教育 */ public class ObjectTest { public static void main(String[] args) { Dog dog01=new Dog("喬治01"); Dog dog02=new Dog("喬治02"); System.out.println("dog01對(duì)比dog02 "+(dog01==dog02));//false //equals()方法的底層默認(rèn)還是利用==實(shí)現(xiàn)的 System.out.println("dog01對(duì)比dog02 "+(dog01.equals(dog02)));//false } }
從上面的案例中,我們也可以證明,equals()方法用于處理引用類(lèi)型的變量,默認(rèn)比較的是兩個(gè)引用類(lèi)型的變量地址是否相等。
4. getClass()方法
4.1 簡(jiǎn)介
getClass()方法可以用于獲取對(duì)象運(yùn)行時(shí)的字節(jié)碼類(lèi)型,得到該對(duì)象的運(yùn)行時(shí)的真實(shí)類(lèi)型。該方法屬于Java的反射機(jī)制,其返回值是Class類(lèi)型,例如 Class c = obj.getClass();。
通過(guò)對(duì)象c,我們可以進(jìn)一步獲取該對(duì)象的所有成員方法,每個(gè)成員方法都是一個(gè)Method對(duì)象。我們也可以獲取該對(duì)象的所有成員變量,每個(gè)成員變量都是一個(gè)Field對(duì)象。同樣的,我們也可以獲取該對(duì)象的構(gòu)造函數(shù),構(gòu)造函數(shù)則是一個(gè)Constructor對(duì)象。
4.2 案例
/** * @author 一一哥Sun * 千鋒教育 */ public class ObjectTest { public static void main(String[] args) { //判斷運(yùn)行時(shí)d對(duì)象和c對(duì)象是否是同一個(gè)類(lèi)型 Animal d = new Dog(); Animal c = new Cat(); //方式1:通過(guò)instanceof關(guān)鍵字判斷 if((d instanceof Dog && c instanceof Dog) ||(d instanceof Cat && c instanceof Cat)) { System.out.println("是同一個(gè)類(lèi)型"); }else { System.out.println("不是同一個(gè)類(lèi)型"); } //方式2:通過(guò)getClass方法判斷 if(d.getClass() == c.getClass()) { System.out.println("是同一個(gè)類(lèi)型"); }else { System.out.println("不是同一個(gè)類(lèi)型"); } } }
從上面的代碼案例中,我們可以得知,getClass方法用于返回該對(duì)象的真實(shí)類(lèi)型(運(yùn)行時(shí)的類(lèi)型),可以根據(jù)對(duì)象的字節(jié)碼來(lái)判斷兩個(gè)對(duì)象是否是同一個(gè)對(duì)象。
5. toString()方法
5.1 簡(jiǎn)介
toString()方法可以說(shuō)是一個(gè)進(jìn)行“自我描述”的方法,可以返回某個(gè)對(duì)象的字符串,當(dāng)要輸出某個(gè)實(shí)例對(duì)象的信息時(shí),我們可以通過(guò)重寫(xiě)該方法來(lái)輸出自我描述的信息。該方法通常只是為了方便輸出本類(lèi)的描述信息,比如執(zhí)行System.out.println("xyz")
這樣的日志語(yǔ)句。一般情況下,當(dāng)程序要輸出一個(gè)對(duì)象或者把某個(gè)對(duì)象和字符串進(jìn)行連接運(yùn)算時(shí),系統(tǒng)就會(huì)自動(dòng)調(diào)用該對(duì)象的toString()方法返回該對(duì)象的字符串表示。
Object類(lèi)的toString()方法會(huì)返回“運(yùn)行時(shí)的類(lèi)名@十六進(jìn)制哈希碼”格式的字符串,但很多類(lèi)都重寫(xiě)了 Object類(lèi)的toString()方法,用于返回可以表述該對(duì)象信息的字符串。
5.2 案例
/** * @author 一一哥Sun * 千鋒教育 */ public class Dog implements Animal{ private String name; public Dog() {} public Dog(String name) { this.name = name; } @Override public void eat() { System.out.println("小狗"+this.name+"狗愛(ài)吃骨頭"); } //@Override //public String toString() { //return "Dog name= " + name; //} } public class ObjectTest { public static void main(String[] args) { Dog dog=new Dog("喬治"); System.out.println("dog一號(hào)="+dog); System.out.println("dog二號(hào)="+dog.toString()); } }
上述代碼執(zhí)行結(jié)果如下圖所示:
從上面程序的運(yùn)行結(jié)果可以發(fā)現(xiàn),默認(rèn)情況下,對(duì)象帶不帶toString()方法,其最終的輸出結(jié)果是一樣的,即對(duì)象輸出時(shí)一定會(huì)調(diào)用 Object類(lèi)中的 toString()方法打印內(nèi)容,所以我們可以利用此特性來(lái)通過(guò) toString()方法取得一些對(duì)象的信息。
6. wait()、wait(long)、wait(long,int)、notify()、notifyAll()方法
這幾個(gè)函數(shù)體現(xiàn)的是Java的多線程機(jī)制,一般是結(jié)合synchronize語(yǔ)句使用。
- wait()用于讓當(dāng)前線程失去操作權(quán)限,當(dāng)前線程進(jìn)入等待序列;
- notify()用于隨機(jī)通知一個(gè)持有對(duì)象的鎖的線程獲取操作權(quán)限;
- wait(long) 和wait(long,int)用于設(shè)定下一次獲取鎖的距離當(dāng)前釋放鎖的時(shí)間間隔;
- notifyAll()用于通知所有持有對(duì)象的鎖的線程獲取操作權(quán)限。
這幾個(gè)方法我們后面在分析多線程的面試題時(shí)再細(xì)說(shuō),此處先僅做了解。
7. finalize()方法
7.1 簡(jiǎn)介
finalize()方法在進(jìn)行垃圾回收的時(shí)候會(huì)用到,主要是在垃圾回收時(shí),用于作為確認(rèn)該對(duì)象是否確認(rèn)被回收的一個(gè)標(biāo)記。我們?cè)谑褂胒inalize()方法時(shí)要注意:
- finalize方法不一定會(huì)執(zhí)行,只有在該方法被重寫(xiě)的時(shí)候才會(huì)執(zhí)行;
- finalize方法只會(huì)被執(zhí)行一次;
- 對(duì)象可以在finalize方法中獲得自救,避免自己被垃圾回收,同樣的自救也只能進(jìn)行一次;
- 不推薦Java程序員手動(dòng)調(diào)用該方法,因?yàn)閒inalize方法代價(jià)很大。
7.2 案例
為了測(cè)試出finalize()方法的作用,小編給大家設(shè)計(jì)了如下案例:
/** * @author 一一哥Sun * 千鋒教育 */ public class Dog implements Animal{ private String name; public Dog() {} public Dog(String name) { this.name = name; } @Override public void eat() { System.out.println("小狗"+this.name+"狗愛(ài)吃骨頭"); } //復(fù)寫(xiě)finalize方法 @Override protected void finalize() throws Throwable { super.finalize();//不要?jiǎng)h除這行代碼 System.out.println("finalize方法執(zhí)行了"); } }
然后我們對(duì)Dog對(duì)象進(jìn)行回收測(cè)試:
public class ObjectTest { public static void main(String[] args) { Dog dog=new Dog("喬治"); //手動(dòng)將對(duì)象標(biāo)記為垃圾對(duì)象 dog = null; //觸發(fā)垃圾回收器,回收垃圾對(duì)象 System.gc(); } }
要想確保finalize()方法的執(zhí)行,我們首先需要在相關(guān)對(duì)象中重新finalize()方法,然后將待回收的對(duì)象手動(dòng)標(biāo)記為null,最后再手動(dòng)調(diào)用gc()方法,這樣才有可能確保finalize()方法一定執(zhí)行。
三. 結(jié)語(yǔ)
至此,就把Object類(lèi)給大家介紹完畢了,這個(gè)類(lèi)的內(nèi)容并不是很難,主要是掌握幾個(gè)常用的方法就可以了,尤其是equals()、hashCode()、toString()、getClass()等方法。
以上就是Java中Object用法詳解的詳細(xì)內(nèi)容,更多關(guān)于Java Object的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java二進(jìn)制運(yùn)算基礎(chǔ)知識(shí)點(diǎn)詳解
在本文里小編給大家分享了關(guān)于java二進(jìn)制運(yùn)算基礎(chǔ)知識(shí)點(diǎn)以及實(shí)例代碼內(nèi)容,需要的朋友們參考學(xué)習(xí)下。2019-08-08Java實(shí)現(xiàn)簡(jiǎn)單的分頁(yè)功能
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的分頁(yè)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Java實(shí)現(xiàn)高校教務(wù)系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)高校教務(wù)系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08輸出java進(jìn)程的jstack信息示例分享 通過(guò)線程堆棧信息分析java線程
通過(guò)ps到j(luò)ava進(jìn)程號(hào)將進(jìn)程的jstack信息輸出。jstack信息是java進(jìn)程的線程堆棧信息,通過(guò)該信息可以分析java的線程阻塞等問(wèn)題。2014-01-01java中的類(lèi)型擦除type?erasure示例詳解
泛型是java從JDK?5開(kāi)始引入的新特性,泛型的引入可以讓我們?cè)诖a編譯的時(shí)候就強(qiáng)制檢查傳入的類(lèi)型,從而提升了程序的健壯度,泛型可以用在類(lèi)和接口上,在集合類(lèi)中非常常見(jiàn),本文將會(huì)講解泛型導(dǎo)致的類(lèi)型擦除2023-09-09