摘要:最近經(jīng)過某大佬的建議準(zhǔn)備閱讀一下的源碼來提升一下自己所以開始寫源碼分析的文章閱讀版本為目錄結(jié)構(gòu)圖構(gòu)造器方法方法方法方法方法方法結(jié)構(gòu)圖類構(gòu)造器類構(gòu)造器是創(chuàng)建對象的方法之一。還有一種情況是兩個元素不相同,但是相同,這就是哈希碰撞。
最近經(jīng)過某大佬的建議準(zhǔn)備閱讀一下JDK的源碼來提升一下自己
所以開始寫JDK源碼分析的文章
閱讀JDK版本為1.8
目錄
Object結(jié)構(gòu)圖
構(gòu)造器
equals 方法
getClass 方法
hashCode 方法
toString 方法
finalize 方法
registerNatives 方法
1. Object結(jié)構(gòu)圖 2. 類構(gòu)造器??類構(gòu)造器是創(chuàng)建Java對象的方法之一。一般我們都使用new關(guān)鍵字來進(jìn)行實例,還可以在構(gòu)造器中進(jìn)行相應(yīng)的初始化操作。
??在一個Java類中必須存在一個構(gòu)造器,如果沒有添加系統(tǒng)在編譯時會默認(rèn)創(chuàng)建一個無參構(gòu)造。
/*實例一個Object對象*/ Object obj = new Object()3. equals 方法
??在面試中面試官經(jīng)常會問 equals() 方法和 == 運算符的區(qū)別,== 運算符用于比較基本類型的值是否相同而 equals 用于比較兩個對象是否相等,那么有個問題來了,兩個對象怎么才算是相等的呢。
看object中的equals實現(xiàn)
public boolean equals(Object obj) { return (this == obj); }
在Object中equals和==是等價的。所以在Object中兩個對象的引用相同,那么一定就是相同的。在我們自定義對象的時候一定要重寫equals方法。我參考了以下網(wǎng)上的資料來分析一下String中重寫的 equals方法:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
String 是引用類型,比較時不能比較引用是否相等,重點是字符串的內(nèi)容是否相等。所以 String 類定義兩個對象相等的標(biāo)準(zhǔn)是字符串內(nèi)容都相同。
在Java規(guī)范中,對 equals 方法的使用必須遵循以下幾個原則:
自反性:對于任何非空引用值 x,x.equals(x) 都應(yīng)返回 true。
對稱性:對于任何非空引用值 x 和 y,當(dāng)且僅當(dāng) y.equals(x) 返回 true 時,x.equals(y) 才應(yīng)返回 true。
傳遞性:對于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 應(yīng)返回 true。
一致性:對于任何非空引用值 x 和 y,多次調(diào)用 x.equals(y) 始終返回 true 或始終返回 false,前提是對象上 equals 比較中所用的信息沒有被修改
對于任何非空引用值 x,x.equals(null) 都應(yīng)返回 false
下面定義一個類,在這個類中重寫equals方法 對象屬性相同則相等 否則不相等
public class Student { private String name; /** * 無參構(gòu)造方法 */ public Student() { } /** * 無參構(gòu)造方法 */ public Student(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object obj) { //引用相同 兩個對象肯定是相同的 if(this==obj){ return true; } //對象等于空 或者不是Student 是不想等的 if(obj==null || !(obj instanceof Student)){ return false; } //轉(zhuǎn)為Student對象 Student student = (Student)obj; //屬性相同 返回true return this.getName()==student.getName(); } }
然后創(chuàng)建一個測試類來進(jìn)行測試:
Student t1 = new Student("yes"); Student t2 = new Student("slm"); System.out.println("對象不同 屬性不同 == "+(t1==t2)); System.out.println("對象不同 屬性不同 equals "+(t1.equals(t2))); Student t3 = new Student("slm"); System.out.println("對象不同 屬性相同"+(t2.equals(t3)));
輸出結(jié)果:
對象不同 屬性不同 == false
對象不同 屬性不同 equals false
對象不同 屬性相同true
現(xiàn)在可以看出 如果在這里不重寫equals方法的話永遠(yuǎn)只會執(zhí)行Object的equals也就是通過==對比對象引用地址是否相同。
下面再看一個例子,這個時候如果出現(xiàn)一個Student的子類我們在對比一下
/** * @Author: sunluomeng * @CreateTime: 2019-06-06 23:35 * @Description: */ public class Language extends Student{ private String name; /** * 無參構(gòu)造 */ public Language(){ } /** * 有參構(gòu)造 * @param name */ public Language(String name){ this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object obj) { //引用相同 兩個對象肯定是相同的 if(this==obj){ return true; } //對象等于空 或者不是Student 是不想等的 if(obj==null || !(obj instanceof Language)){ return false; } //轉(zhuǎn)為Student對象 Language language = (Language)obj; //屬性相同 返回true return this.getName()==language.getName(); } }
這個時候我們的新創(chuàng)建的Language類繼承Student然后創(chuàng)建兩個對象去做比較
輸出結(jié)果:
父類對比子類 屬性相同---true
子類對比父類 屬性相同---false
可以看出父類去對比子類既 student.equals(language) 結(jié)果為true 而子類去對比父類 既 language.equals(student) 返回false
這樣的話就違反了問哦們上面說到的對稱性
對于任何非空引用值 x 和 y,當(dāng)且僅當(dāng) y.equals(x) 返回 true 時,x.equals(y) 才應(yīng)返回 true
如果y是Student x 是Language
那么現(xiàn)在就是 y.equals(x) 等于true 反過來x.equals(y)也應(yīng)該返回true,但是現(xiàn)在為什么會返回false呢?
先來看一下代碼
我們在判斷的時候使用了instanceof關(guān)鍵字來判斷運行的時候是否是指定的類型
java 中的instanceof 運算符是用來在運行時指出對象是否是特定類的一個實例。instanceof通過返回一個布爾值來指出,這個對象是否是這個特定類或者是它的子類的一個實例。
這樣的話也就是說 Language是Student的子類 在用instanceof判斷的時候是返回true,而Language雖然是繼承Student 但是使用instanceof判斷的時候會發(fā)現(xiàn) Language和Student的類型不同 然后Student也不是Language的子類所以會返回false。
而解決的辦法就是
然后我們在運行一下剛剛的代碼
輸出結(jié)果:
父類對比子類 屬性相同---false
子類對比父類 屬性相同---false
完美解決,滿足對稱性
注意:使用getClass是要根據(jù)情況而定,使用getClass 不符合多態(tài)的定義
那什么時候使用instanceof,什么時候使用getClass呢?
如果子類能夠擁有自己的相等概念,則對稱性需求將強制采用 getClass 進(jìn)行檢測。
如果有超類決定相等的概念,那么就可以使用 instanceof 進(jìn)行檢測,這樣可以在不同的子類的對象之間進(jìn)行相等的比較。
還有就是一定要注意無論何時重寫此方法,通常都必須重寫hashCode方法,以維護(hù)hashCode方法的一般約定,該方法聲明相等對象必須具有相同的哈希代碼。
4.getClass 方法我們首先看一下getClass在Object中的實現(xiàn)。
我們看到getClass被native標(biāo)識,這代表這是調(diào)用本地方法實現(xiàn)
關(guān)于native更多請百度。native是由操作系統(tǒng)幫我們實現(xiàn)
文檔說明的是調(diào)用getClass返回一個運行時的類。什么意思呢 我們看下面的代碼實現(xiàn)。
打印結(jié)果:
可以看出getClass是返回一個運行時的對象。class是返回編譯的類對象
可以看到getClass方法被final修飾,說明此方法不能被重寫。
先看一下hashCode在Object中的實現(xiàn):
hashCode也是一個被native修飾的本地方法
注釋說明的是返回該對象的哈希值。那么它有什么作用呢?
主要是保證基于散列的集合,如HashSet、HashMap以及HashTable等,在插入元素時保證元素不可重復(fù),同時為了提高元素的插入刪除便利效率而設(shè)計;主要是為了查找的便捷性而存在。
就比如使用Set進(jìn)行舉例子。
Set集合是不可重復(fù)的,如果每次添加數(shù)據(jù)都使用equals去做對比的話,插入十萬條數(shù)據(jù)就要對比十萬次效率是非常慢的。
所以在添加數(shù)據(jù)的時候使用了哈希表,哈希算法也稱之為散列算法,當(dāng)添加一個值的時候先算出它的哈希值根據(jù)算出的哈希值將數(shù)據(jù)插入指定位置。這樣的話就避免了一直調(diào)用equals造成的效率隱患。同時有以下條件:
如果位置為空則直接添加
如果位置不為空,判斷兩個元素是否相同如果相同則不存儲。
還有一種情況是兩個元素不相同,但是hashCode相同,這就是哈希碰撞。
如果發(fā)生了hash key相同的情況就在相同的元素創(chuàng)建一個鏈表。把所有相同的元素存放在鏈表中。
可以看出T1的哈希和T2相同,但是元素不同,所以現(xiàn)在會形成一個鏈來存儲。
先看toString的實現(xiàn)
可以看出toString是返回的類名加16進(jìn)制無符號整數(shù)形式返回此哈希碼的字符串表示形式。
運行輸出結(jié)果:
直接輸出對象和使用toString是一樣的
如果想要toString輸出屬性內(nèi)容則需要重寫toString方法
源碼中實現(xiàn)方法:
finalize用戶垃圾回收是由JVM調(diào)用。
源碼實現(xiàn):
上面說到native是調(diào)用本地實現(xiàn)方法,而registerNatives則是對本地方法注冊,裝載本地庫。在Object初始化時執(zhí)行。
還有notify()/notifyAll()/wait()等寫到多線程的時候在做分析
最后小弟不才,如有錯誤請指出。喜歡請關(guān)注,慢慢更新JDK源碼閱讀筆記
小弟公眾號,亂敲代碼。歡迎點贊,關(guān)注
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/75382.html
摘要:簡介繼續(xù)分析源碼,上一篇文章把的分析完畢。本文開始分析簡單的介紹一下。存儲的元素是無序的并且允許使用空的元素。 1.簡介 繼續(xù)分析源碼,上一篇文章把HashMap的分析完畢。本文開始分析HashSet簡單的介紹一下。 HashSet是一個無重復(fù)元素集合,內(nèi)部使用HashMap實現(xiàn),所以HashMap的特征耶繼承了下來。存儲的元素是無序的并且HashSet允許使用空的元素。 HashSe...
摘要:作為條件變量的的不僅可以認(rèn)為內(nèi)嵌了一把鎖,還內(nèi)嵌了一個條件變量。操作條件變量的函數(shù)將當(dāng)前線程在條件變量上阻塞,一般是為了等待其他線程的某件事情執(zhí)行完成。其它裝箱類其它裝箱類的代碼這里就不分析了。重點關(guān)注下各裝箱類的緩存范圍。 jdk源碼讀到現(xiàn)在這里,重要的集合類也讀了一部分了。集合類再往下讀的話,就要涉及到兩個方向。第一,是比較典型的但是不常用的數(shù)據(jù)結(jié)構(gòu),這部分我準(zhǔn)備將數(shù)據(jù)結(jié)構(gòu)復(fù)習(xí)、回...
摘要:畢業(yè)兩個星期了,開始成為一名正式的碼農(nóng)了。將指定位置的數(shù)據(jù)移除。但是問題是,為時,并不是直接一個大小為的數(shù)組,而是使用靜態(tài)變量來代替。此外,函數(shù)還做了越界檢查。返回迭代器,與之有一個搭配的輔助類。 畢業(yè)兩個星期了,開始成為一名正式的java碼農(nóng)了。一直對偏底層比較感興趣,想著深入自己的java技能,看書、讀源碼、總結(jié)、造輪子實踐都是付諸行動的方法。說到看源碼,就應(yīng)該由簡入難,逐漸加深,...
摘要:當(dāng)一個值中要存儲到的時候會根據(jù)的值來計算出他的,通過哈希來確認(rèn)到數(shù)組的位置,如果發(fā)生哈希碰撞就以鏈表的形式存儲在源碼分析中解釋過,但是這樣如果鏈表過長來的話,會把這個鏈表轉(zhuǎn)換成紅黑樹來存儲。 正文開始 注:JDK版本為1.8 HashMap1.8和1.8之前的源碼差別很大 目錄 簡介 數(shù)據(jù)結(jié)構(gòu) 類結(jié)構(gòu) 屬性 構(gòu)造方法 增加 刪除 修改 總結(jié) 1.HashMap簡介 H...
摘要:唐老師,回答道讀源碼是要建立在你的基礎(chǔ)經(jīng)驗足夠的情況下。除了自己去閱讀源碼之外,比如學(xué)習(xí)某個類的時候,可以專門結(jié)合一些優(yōu)質(zhì)的博客針對性的對比學(xué)習(xí),并查漏補缺。制定源碼學(xué)習(xí)計劃。多調(diào)試,跟蹤源碼。如若有好的學(xué)習(xí)方法,可以留言一起交流學(xué)習(xí)。 序言:目前看一看源碼,來提升自己的技術(shù)實力。同時現(xiàn)在好多面試官都喜歡問源碼,問你是否讀過JDK源碼等等? 針對如何閱讀源碼,也請教了我的老師。下面就先...
閱讀 1093·2021-11-22 14:56
閱讀 1530·2019-08-30 15:55
閱讀 3371·2019-08-30 15:45
閱讀 1666·2019-08-30 13:03
閱讀 2879·2019-08-29 18:47
閱讀 3341·2019-08-29 11:09
閱讀 2649·2019-08-26 18:36
閱讀 2624·2019-08-26 13:55