jdk源碼(一):Object 一切類的根本莹捡!

一、概述

Object 類是位于java.lang包下面的類扣甲,是所有類的父類篮赢,是類層級的根,所有的對象琉挖,包括數(shù)組启泣、也都實現(xiàn)了Object的方法。

二示辈、源碼分析

(1) registerNatives方法與靜態(tài)代碼塊寥茫,源碼如下:
private static native void registerNatives();
    static {
        registerNatives();
    }

說明:

  • native 關(guān)鍵字修飾的方法沒有方法體, 這是java調(diào)用其他地方的接口的一個聲明關(guān)鍵字矾麻,意思是這個方法不是java實現(xiàn)的纱耻,native關(guān)鍵字是與c語言聯(lián)合開發(fā)的時候使用 ,為了能夠快速執(zhí)行代碼射富。hashCode()方法也用到了膝迎,因為涉及內(nèi)存,C語言操作比java快捷高效胰耗。 值得注意的是限次,native方法不能與abstract方法一起使用,因為native表示這些方法是有實現(xiàn)體的,但是abstract卻表示這些方法是沒有實現(xiàn)體的卖漫,那么兩者矛盾费尽,肯定也不能一起使用。

  • registerNatives()方法的作用是在類加載時羊始,就主動將本地方法連接到調(diào)用方旱幼, 當(dāng)Java程序需要調(diào)用本地方法時就可以直接調(diào)用,而不需要虛擬機(jī)再去定位并鏈接突委。 這樣做的好處有以下4點:

    a. 通過registerNatives方法在類被加載的時候就主動將本地方法鏈接到調(diào)用方柏卤,比當(dāng)方法被使用時再由虛擬機(jī)來定位和鏈接更方便有效。

    b. 如果本地方法在程序運(yùn)行中更新了匀油,可以通過調(diào)用registerNative方法進(jìn)行更新缘缚。

    c. Java程序需要調(diào)用一個本地應(yīng)用提供的方法時,因為虛擬機(jī)只會檢索本地動態(tài)庫敌蚜,因而虛擬機(jī)是無法定位到本地方法實現(xiàn)的桥滨,這個時候就只能使用registerNatives()方法進(jìn)行主動鏈接。

    d. 通過registerNatives()方法弛车,在定義本地方法實現(xiàn)的時候齐媒,可以不遵守 JNI命名規(guī)范 。 JNI命名規(guī)范要求本地方法名由“包名”+“方法名”構(gòu)成 纷跛。

(2) getClass()方法源碼如下:
 public final native Class<?> getClass();

我們可以看到喻括,getClass方法也是用native關(guān)鍵字修飾的,原因同上贫奠;同時還被final關(guān)鍵字修飾双妨。final修飾的方法是不能被子類重寫的,我們來詳細(xì)分析以下這里的內(nèi)容叮阅。

說明:

  • final修飾符在java中的作用有以下3點:

    a. final修飾的類,為最終類泣特,該類不能被繼承浩姥,如String 類。

    b. final修飾的方法可以被繼承和重載状您,但是不能被重寫勒叠。

    c. final修飾的變量是不能被修改的,是個常量膏孟。

此處就是為了防止任何繼承類改變它的本來含義眯分,希望此方法的行為在繼承期間保持不變,而且不可被覆蓋或是改寫柒桑。

  • getClass()方法能夠獲取此Object的運(yùn)行時類弊决,是利用反射機(jī)制完成的。java中類的生命周期,會經(jīng)歷加載飘诗、連接(驗證与倡、準(zhǔn)備、解析)昆稿、初始化纺座、使用、卸載五個步驟溉潭。加載過程中會經(jīng)歷一個流程java->class-> 內(nèi)存(生成Java.lang.Class文件)净响。生成的.class文件將會在反射中使用。如果有一個實例那么就可以通過實例的getClass()方法獲取該對象的類型類,如果你知道一個類型,那么你可以使用.class()的方法獲得該類型的類型類喳瓣。
(3)hashCode()方法源碼如下:
public native int hashCode();

hashCode()方法返回當(dāng)前對象運(yùn)行時的hash碼馋贤,是用于支持散列表數(shù)據(jù)結(jié)構(gòu),因為散列表在進(jìn)行數(shù)據(jù)存儲時依賴hash碼決定數(shù)據(jù)存儲的邏輯位置夫椭。在程序運(yùn)行中掸掸,無論什么情況下,相同的對象對應(yīng)的hash碼一定是相同的蹭秋。但是不同的對象有可能會返回相同的hash碼扰付。那么其實也代表如果兩個對象的hash碼不一致,這兩個對象一定是不同的仁讨。hashCode方法還具有以下3個特點:

  • 在 Java 應(yīng)用程序執(zhí)行期間羽莺,在對同一對象多次調(diào)用 hashCode 方法時,必須一致地返回相同的整數(shù)洞豁,前提是將對象進(jìn)行 equals 比較時所用的信息沒有被修改盐固。從某一應(yīng)用程序的一次執(zhí)行到同一應(yīng)用程序的另一次執(zhí)行,該整數(shù)無需保持一致丈挟。

  • 如果根據(jù) equals(Object) 方法刁卜,兩個對象是相等的,那么對這兩個對象中的每個對象調(diào)用 hashCode 方法都必須生成相同的整數(shù)結(jié)果曙咽。

  • 如果根據(jù) equals(java.lang.Object) 方法蛔趴,兩個對象不相等,那么對這兩個對象中的任一對象上調(diào)用 hashCode方法不要求一定生成不同的整數(shù)結(jié)果例朱,為不相等的對象生成不同整數(shù)結(jié)果可以提高哈希表的性能孝情。

hashCode()方法有時也會返回相同的hash值,就是我們所說的hash沖突洒嗤。因為盡管虛擬機(jī)在運(yùn)行過程中箫荡,不同的對象的地址一定是不同的,但是由于hashcode需要固定25位或者31位渔隶,那么就導(dǎo)致真正的hashcode值需要在對象地址上做一定的操作羔挡。從而將一個大范圍區(qū)間的值映射到一個小范圍區(qū)間的值(hashcode的計算過程),這樣的操作必定會導(dǎo)致一部分?jǐn)?shù)據(jù)的計算結(jié)果會重復(fù)。

(4) equals()方法源碼如下:
public boolean equals(Object obj) {
    return (this == obj);
}

Object中的equals方法默認(rèn)比較當(dāng)前對象的引用婉弹,是直接判斷this和obj本身的值是否相等睬魂,即用來判斷調(diào)用equals的對象和形參obj所引用的對象是否是同一對象,所謂同一對象就是指內(nèi)存中同一塊存儲單元镀赌,如果this和obj指向的是同一塊內(nèi)存對象氯哮,則返回true,如果this和obj指向的不是同一塊內(nèi)存,則返回false商佛,注意:即便是內(nèi)容完全相等的兩塊不同的內(nèi)存對象喉钢,也返回false。

值得注意的是良姆,String類重寫了此方法肠虽,源代碼如下:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = offset;
            int j = anotherString.offset;
            while (n– != 0) {
                if (v1[i++] != v2[j++])
                    return false;
            }
            return true;
        }
    }
    return false;
}
(5) toString()方法源碼如下:
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

說明:

  • getName() 以String形式返回類對象的名稱(包括包名) 。

  • Integer.toHexString(hashCode()) 以對象的哈希碼為參數(shù)玛追,以16進(jìn)制無符號整數(shù)形式返回此哈希碼的字符串表示形式恤筛。

(6) 我們進(jìn)入到toHexString源碼中讯检,如下:
public static String toHexString(int i) {
    return toUnsignedString0(i, 4);
}

private static String toUnsignedString0(int val, int shift) {
    // assert shift > 0 && shift <=5 : "Illegal shift value";
    //為了計算val值對應(yīng)的二進(jìn)制數(shù)中除去首部0的個數(shù)后剩下的有效位數(shù)mag颂翼。
    //Integer.SIZE: int在jvm中占4個字節(jié)枪蘑,共32位;
    //首部0的個數(shù):是指從左邊第一個位置開始累加0的個數(shù)陆馁,一直加到第一個非零值找颓;
    //方法numberOfLeadingZeros() 就是計算首部0的個數(shù)。
    int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); 
    //計算 i 的有效二進(jìn)制的位數(shù)所需要占的字符數(shù)(即對應(yīng)的16進(jìn)制的有效位數(shù))叮贩,toString()方法是以16進(jìn)制返回击狮,這里就以16進(jìn)制來解釋。
    int chars = Math.max(((mag + (shift - 1)) / shift), 1);
    char[] buf = new char[chars];
    //將val值以16進(jìn)制數(shù)形式存進(jìn)buf數(shù)組中益老。
    formatUnsignedInt(val, shift, buf, 0, chars);
 
    // Use special constructor which takes over "buf".
    return new String(buf, true);
}
(7) 我們再進(jìn)入到numberOfLeadingZeros()方法中查看源碼彪蓬,如下:
public static int numberOfLeadingZeros(int i) {
    // HD, Figure 5-6
    if (i == 0)
        return 32;
    // 初始首部0的個數(shù):1 假定最高位有一個0,最后的時候會根據(jù)最高位是否位零補(bǔ)償返回
    int n = 1;
    // 下面的代碼主要是為了找出左邊第一個非零值的位置
    // 采用二分查找的方法捺萌,首先將 i 無符號右移16位后寞焙,有兩種情況(a, b):
    // a. 右移后 = 0,則第一個非零值出現(xiàn)在低16位互婿,那么 i 首位至少有16個0(n=17),
    // 同時將 i 左移16位后賦值給自己(將低16位移到了高16位辽狈,這樣可以使兩種保持同一的狀態(tài)進(jìn)行后續(xù)判斷)慈参;
    // b. 右移后!=0刮萌,則第一個非零值出現(xiàn)在高16位(n=1)驮配,繼續(xù)在高16位中尋找;
    // 將高16位繼續(xù)分為 高8位和低8位進(jìn)行判斷,一直二分到還有兩位的時候壮锻;
    // 最后 i 無符號右移31位(結(jié)果要么是0要么是1)琐旁,如果右移后為0,說明此時的最高位為0猜绣,無需補(bǔ)償灰殴,直接返回 n;
    // 如果右移后是1掰邢,則說明最高位不為0牺陶,需補(bǔ)償,返回 n-1辣之;
    if (i >>> 16 == 0) { n += 16; i <<= 16; }
    if (i >>> 24 == 0) { n +=  8; i <<=  8; }
    if (i >>> 28 == 0) { n +=  4; i <<=  4; }
    if (i >>> 30 == 0) { n +=  2; i <<=  2; }
    n -= i >>> 31;
    return n;
}

我們可以看出掰伸, 源碼應(yīng)用了二分查找,先把32位整形分為高16位和低16位查找非零數(shù)怀估,在對高16位進(jìn)行或低16位進(jìn)行二分狮鸭,以此類推,直到找到左邊第一個非零值的位置多搀。

(8) 最后我們在進(jìn)入到formatUnsignedInt()方法中歧蕉,源碼如下:

 static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
    int charPos = len;
    int radix = 1 << shift;
    int mask = radix - 1;
    do {
        buf[offset + --charPos] = Integer.digits[val & mask];
        val >>>= shift;
    } while (val != 0 && charPos > 0);

    return charPos;
}

/**
 * All possible chars for representing a number as a String
 */
final static char[] digits = {
    '0' , '1' , '2' , '3' , '4' , '5' ,
    '6' , '7' , '8' , '9' , 'a' , 'b' ,
    'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
    'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
    'o' , 'p' , 'q' , 'r' , 's' , 't' ,
    'u' , 'v' , 'w' , 'x' , 'y' , 'z'
};

源碼中,先進(jìn)行按位與運(yùn)算酗昼,算出val的二進(jìn)制中低四位對應(yīng)的10進(jìn)制的值廊谓,在數(shù)組中獲取對應(yīng)的16進(jìn)制值,存進(jìn)buf字符數(shù)組中麻削;后將val無符號右移4位蒸痹,保持在最低四位與mask進(jìn)行按位與運(yùn)算,在數(shù)組中獲取對應(yīng)的16進(jìn)制值呛哟,存進(jìn)buf字符數(shù)組中叠荠,以此循環(huán),直到val=0或者charPos<=0后退出循環(huán)扫责。digits 數(shù)組中表示 16 進(jìn)制的是前 16 個榛鼎,數(shù)組下標(biāo)最大為 15, Integer.digits[val & mask] 保證了每次從最低四位開始匹配對應(yīng)的 16 進(jìn)制數(shù)鳖孤≌哂椋總結(jié)起來步驟如下:

a. 計算 hashCode 對應(yīng)的有效二進(jìn)制位數(shù) n

b. n 位二進(jìn)制數(shù)對應(yīng)的 16 進(jìn)制數(shù)的個數(shù),即對應(yīng)的所要占據(jù)的字符數(shù)

c. 將 n 位二進(jìn)制數(shù)轉(zhuǎn)換為 16 進(jìn)制數(shù)并存入字符數(shù)組中

d. 最后返回 16 進(jìn)制無符號整數(shù)的字符串

(9) clone()方法源碼如下:

protected native Object clone() throws CloneNotSupportedException;

說明:

  • 如果一個類沒有子類,那么這個類的protected方法就只能在這個類所在包下的類中被調(diào)用,如果這個方法有子類,那么這個子類繼承父類的protected方法也就只能在這個子類內(nèi)部使用苏揣。

  • 一個類想要使用clone()方法的話黄鳍,就要先實現(xiàn)克隆接口Cloneable。Cloneable接口進(jìn)去發(fā)現(xiàn)平匈,沒有任何內(nèi)容框沟,這個接口是一個JVM標(biāo)記接口藏古。如果在沒有實現(xiàn) Cloneable接口的實例上調(diào)用Object的clone()方法,則會導(dǎo)致拋出CloneNotSupportedException異常忍燥。

  • 標(biāo)記接口是計算機(jī)科學(xué)中的一種設(shè)計思路拧晕,用于給那些面向?qū)ο蟮木幊陶Z言描述對象。因為編程語言本身并不支持為類維護(hù)元數(shù)據(jù)梅垄,而標(biāo)記接口可以用作描述類的元數(shù)據(jù)厂捞,彌補(bǔ)了這個功能上的缺失。對于實現(xiàn)了標(biāo)記接口的類哎甲,我們就可以在運(yùn)行時通過反射機(jī)制去獲取元數(shù)據(jù)蔫敲。

  • 淺克隆是在克隆時,基本數(shù)據(jù)類型直接拷貝炭玫,而引用類型只能拷貝引用地址奈嘿。

  • 深克隆是重寫clone方法,每次調(diào)用時吞加, 先克隆一下引用類型裙犹,把克隆后的引用類型設(shè)置給克隆的對象

(10) finalize()方法源碼如下:
 protected void finalize() thirows Throwable { }

此方法是垃圾回收時調(diào)用的方法,存在以下兩種情況可能會調(diào)用:

  • 對象置為空后衔憨,JVM內(nèi)存不充足時會回收

  • 可以主動建議JVM回收Runtime.getRunTime().gc()

(11) notify()方法

學(xué)習(xí)多線程時詳細(xì)介紹

(12) notifyAll()方法

學(xué)習(xí)多線程時詳細(xì)介紹

(13) wait()方法

學(xué)習(xí)多線程時詳細(xì)介紹

三叶圃、總結(jié)

Object類作為所有類的根類,學(xué)好它的源碼是非常必要的践图,也能帶給我們更多的思考掺冠。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市码党,隨后出現(xiàn)的幾起案子德崭,更是在濱河造成了極大的恐慌,老刑警劉巖揖盘,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眉厨,死亡現(xiàn)場離奇詭異,居然都是意外死亡兽狭,警方通過查閱死者的電腦和手機(jī)憾股,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箕慧,“玉大人服球,你說我怎么就攤上這事〉呓梗” “怎么了斩熊?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蒸健。 經(jīng)常有香客問我座享,道長,這世上最難降的妖魔是什么似忧? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任渣叛,我火速辦了婚禮,結(jié)果婚禮上盯捌,老公的妹妹穿的比我還像新娘淳衙。我一直安慰自己,他們只是感情好饺著,可當(dāng)我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布箫攀。 她就那樣靜靜地躺著,像睡著了一般幼衰。 火紅的嫁衣襯著肌膚如雪靴跛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天渡嚣,我揣著相機(jī)與錄音梢睛,去河邊找鬼。 笑死识椰,一個胖子當(dāng)著我的面吹牛绝葡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腹鹉,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼藏畅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了功咒?” 一聲冷哼從身側(cè)響起愉阎,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎航瞭,沒想到半個月后诫硕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刊侯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年章办,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滨彻。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡藕届,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亭饵,到底是詐尸還是另有隱情休偶,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布辜羊,位于F島的核電站踏兜,受9級特大地震影響词顾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜碱妆,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一肉盹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疹尾,春花似錦上忍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至繁成,卻和暖如春吓笙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背朴艰。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工观蓄, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人祠墅。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓侮穿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親毁嗦。 傳聞我的和親對象是個殘疾皇子亲茅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,562評論 2 349