論JDK源碼的重要性:一道面試題引發(fā)的無限思考

那我們就看一下這道面試題是什么呢龄毡?差不多是這樣子的面試題

題目的意思是:定義了兩個Integer類型變量赎瑰,通過swap方法交換這兩個變量的值薄声。

看似簡單的題目当船,是不是不知道從何下手,我猜想有些大家第一想到的是這樣的解法:來看代碼:

運行結(jié)果如下:

從結(jié)果來看是錯誤的默辨,不能解決我們的問題德频。為什么?

在分析之前缩幸,我們先介紹一下Java訪問對象的方式壹置。在 Java 堆中還必須包含能查找到此對象類型數(shù)據(jù)(如對象類型、父類表谊、 實現(xiàn)的接口钞护、方法等)的地址信息,這些類型數(shù)據(jù)則存儲在方法區(qū)中爆办。

既然java棧中的是對象的引用难咕,那么我們?nèi)绾问褂脤ο竽牵髁鞯脑L問方式有兩種:使用句柄和直接指針距辆。

(1)使用句柄:

如果使用句柄訪問方式余佃, Java 堆中將會劃分出一塊內(nèi)存來作為句柄池,reference 中存儲的就是對象的句柄地址跨算,而句柄中包含了對象實例數(shù)據(jù)和類型數(shù)據(jù)各自的具體地址信息咙冗,如圖:

(2)直接指針

如果使用直接指針訪問方式, Java 堆對象的布局中就必須考慮如何放置訪問類型數(shù)據(jù)的相關(guān)信息漂彤, reference 中直接存儲的就是對象地址雾消,如圖:

這兩種對象的訪問方式各有優(yōu)勢,使用句柄訪問方式的最大好處就是 reference 中存儲的是穩(wěn)定的句柄地址挫望,在對象被移動(垃圾收集時移動對象是非常普遍的行為)時只會改變句柄中的實例數(shù)據(jù)指針立润,而 reference 本身不需要被修改。

使用直接指針訪問方式的最大好處就是速度更快媳板,它節(jié)省了一次指針定位的時間開銷桑腮,由于對象的訪問在 Java 中非常頻繁,因此這類開銷積少成多后也是一項非瞅刃遥可觀的執(zhí)行成本破讨。

接著我們回到正題丛晦,這里也是今天要講的第一個知識點:Java的傳值在java中,有兩種傳值方式:一種是按值傳遞提陶,一種是引用傳遞烫沙!

那么,按值傳遞意味著將當(dāng)前的參數(shù)傳遞給方法的時候隙笆,方法中的變量接收的是傳過來變量的副本值(相當(dāng)于拷貝了一份值)锌蓄,因此,我們修改了方法里面的變量的值撑柔,并不會改變外面變量的值瘸爽。

引用傳遞:傳遞的是指向值的地址的指針

那么,請問大家铅忿,這里是按值傳遞還是引用傳遞剪决?好,老司機告訴你們檀训,這里是按值傳遞昼捍,為什么?Integer不是對象嗎肢扯? 對象傳遞不是傳遞的指針嗎妒茬?大家有沒有去看過Integer類的源碼,看看這個類是怎么定義的蔚晨,我們來看下乍钻,實際上面Integer使用的final定義的,也就意味著通過Integer實例化的對象是不能改變的铭腕,跟String是不是差不多银择。所以這里的話,是傳遞的值累舷,我們來畫下圖:

那么浩考,我們首先看一下Java運行時數(shù)據(jù)區(qū)域:

我們一般在開發(fā)中認(rèn)為JVM不過有堆和棧兩部分組成,但是實際的Java 虛擬機在執(zhí)行 Java 程序的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域被盈。這些區(qū)域都有各自的用途析孽,以及創(chuàng)建和銷毀的時間,有的區(qū)域隨著虛擬機進程的啟動而存在只怎,有些區(qū)域則是依賴用戶線程的啟動和結(jié)束而建立和銷毀袜瞬。如下圖:

Java中的內(nèi)存主要分為兩塊把:堆和棧,棧存儲變量本身身堡,堆存儲對象的值邓尤,然后通過棧執(zhí)行堆內(nèi)存地址來建立關(guān)系。

通過swap方法后:意味著,會同樣創(chuàng)建兩個變量num1和num2,他們的值是剛剛拷貝過來的ab的值汞扎,此時內(nèi)存中時怎么變化的呢:

大家季稳,知道為什么會有地址指針這個東西,主要是我們的堆內(nèi)存他主要是存儲的是一些對象澈魄,對象是最占內(nèi)存的景鼠,為了能夠節(jié)省對內(nèi)從的空間,就出現(xiàn)了這種概念一忱。好,講到這里谭确,至少大家應(yīng)該清楚了一點:引用傳遞和按值傳遞的不同帘营。推薦一個學(xué)Java的學(xué)習(xí)裙【六七八,二四一逐哈,五六三】芬迄,無論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進步一起學(xué)習(xí)昂秃!裙內(nèi)有開發(fā)工具禀梳,很多干貨和技術(shù)資料分享

我們再來看,這個Integer他內(nèi)部是如何賦值的肠骆,我們來看下:進入Integer類Ctrl+o搜索Integer構(gòu)造方法:

然后我們發(fā)現(xiàn)這個value定義的是final類型的:

如果他有一個setValue()的方法的話算途,那么我們是是不是可以通過這個方法來改變值,但是Integer并沒有提供蚀腿。也就是說這種方法是行不通的嘴瓤,好,那么我們今天講到第二個知識點:反射有沒有人在做這個題目的時候有沒有想過用反射來實現(xiàn)莉钙?

有想過的廓脆,看有多少人有往這個方面去想,我們剛剛看到Integer類中存在一個value值變量嗎磁玉?對吧停忿,所以我們需要拿到這個value變量然后來改變他的值,對吧蚊伞,那么我們怎么來做席赂,我們可以通過反射的方式拿到這個變量,這個Filed时迫,然后去改變他的值氧枣,對吧。我們來看下怎么寫:

理論上來說别垮,這種方式是一定能夠?qū)崿F(xiàn)我們的要求的.Run下:報錯:“Class com.edu.example.test.Test can not access a member of class java.lang.Integer with modifiers "private final"”

報錯了便监,是不是,那么這又是另外一個知識點:

私有的成員屬性是不能通過反射來賦值的!

那么烧董,如果要強攻毁靶,怎么辦?實際上面逊移,在java反射中预吆,提供了一個叫設(shè)置訪問權(quán)限的東西,我們進入Field類中看下:

然后他里面有一個setAccessible的方法:

這個方法就是用來設(shè)置成員屬性訪問權(quán)限的胳泉。我們看到最后是給obj.override=flag

那么我們在回過頭來看下拐叉,F(xiàn)ield的set方法:

這幾行代碼意味著,也就是說扇商,如果override是false,就會調(diào)用Reflection.quickCheckMemberAccess(clazz, modifiers)來檢查成員屬性的訪問權(quán)限凤瘦。

所以說,我們再來看案铺,這個時候是不是就可以通過設(shè)置setAccessible(true)為true來標(biāo)志不需要訪問權(quán)限的檢查蔬芥。這樣就可以修改value的值了。對不控汉。我們來試驗下:

好笔诵,大家覺得這樣沒問題,結(jié)果如下:

結(jié)果是姑子,a的值確實變了乎婿,但是b的值卻沒有變,首先說明通過這種方式確實可以改變值街佑,但是為什么b的值沒有變化呢次酌?。請問為什么舆乔?我們再回過頭來看看外面的方法岳服,檢查一下,我們定義了:

有沒有發(fā)現(xiàn)什么問題希俩?

Integer是不是一個封裝類型吊宋,而他的值1,2,是不是一個int類型颜武,是一個基本數(shù)據(jù)類型璃搜,那么這里是怎么賦值的呢? 那么我們按照正常來寫是不是這樣子的:

int a = 1;

但是為什么使用Integer也不會報錯了鳞上,好这吻,這就講到了我們又一個知識點:(筆記)

Java中的裝箱和拆箱

裝箱:把基本類型用它們相應(yīng)的引用類型包裝起來,使其具有對象的性質(zhì)篙议。int包裝成Integer唾糯、float包裝成Float;

拆箱:和裝箱相反怠硼,將引用類型的對象簡化成值類型的數(shù)據(jù);

Integer a = 100; // 這是自動裝箱 (編譯器調(diào)用的是static Integer valueOf(int i))

int b = new Integer(100); //這是自動拆箱

那么我們來實際看下,我們耳聽為虛移怯,眼見為實香璃,我們來看下編譯的字節(jié)碼文件:

命令:javap -c Test.class

可以看到:

Jvm他自動做了裝箱操作,看的清清楚楚對吧舟误,對吧

好那么葡秒,我們來看下Integer.valueOf(1):源碼

意味著值大于IntegerCace.low小于IntegerCache.high的話:

會從IntegerCache中獲取,也就是從緩存中取值嵌溢。

那么我們來看下IntegerCache:

也就是說從-128到127直接的所有值眯牧,都是從緩存中獲取。而緩存中的值赖草,是什么時候放進去的学少,是jvm啟動的時候就放進去了,然后分配好內(nèi)存地址疚顷。

你們有沒有發(fā)現(xiàn)旱易,就短短幾行代碼禁偎,怎么就有這么多知識腿堤,是不是都有點感覺不認(rèn)識java了。很神奇吧如暖,哈哈好笆檀,前面這兩行代碼我們分析完了對吧,好盒至,然后酗洒,然后我們把ab的值傳進來,我們再來分析swap中的這段代碼枷遂,好吧樱衷,精華部分就是這段代碼了啊,這是精華部分酒唉,哈哈矩桂,我們來看:斷點到這句

然后按F5進去看下,把IntegerCache里面的值全面拿出來放到notepat++

第一步:是不是需要獲取num2的值痪伦,那么他從下標(biāo)[2+128=130]IntegerCache中獲取值為:130下標(biāo)侄榴,也就是第131個數(shù)字為:2

第二步:field.set(num1,num2),, 意味著第一步先獲取num1在IntegerCache中的值IntegerCache[1+128] =1 ,然后會修改IntegerCache[num1]的值為num2從Integercache中獲取到的值2网沾, 也就是修改為:integerCache[129] = 2

第三步:下一行代碼執(zhí)行

此時癞蚕,再次拿出IntegerCache, 那么下標(biāo)為129,130的值都變成了2辉哥, 此時tmp的值為1桦山,那么從IntegerCache獲取到的值為IntegerCache[1+128=129] ,也就是獲取130行的數(shù),也就是2,所以結(jié)果就是這樣度苔。實際上面和下面這個是一樣的:

從這一句debug進去:發(fā)現(xiàn)走的緩存匆篓,然后從cache中第129個下標(biāo)找到了。

所以寇窑,當(dāng)我們的值是在【-127-128】的時候鸦概,他是從IntegerCache中獲取的。其實甩骏,我們可以這樣來驗證一下:

結(jié)果為:true

結(jié)果為:false

那么窗市,這個當(dāng)時我其實又遇到這個坑,被坑慘了是吧饮笛。哈哈哈咨察。

那么我們怎么解決最后的問題:(最初的面試問題)

1.

2.

3.取巧的方式:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市福青,隨后出現(xiàn)的幾起案子摄狱,更是在濱河造成了極大的恐慌,老刑警劉巖无午,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件媒役,死亡現(xiàn)場離奇詭異,居然都是意外死亡宪迟,警方通過查閱死者的電腦和手機酣衷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來次泽,“玉大人穿仪,你說我怎么就攤上這事∫饣纾” “怎么了啊片?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長玖像。 經(jīng)常有香客問我紫谷,道長,這世上最難降的妖魔是什么御铃? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任碴里,我火速辦了婚禮,結(jié)果婚禮上上真,老公的妹妹穿的比我還像新娘咬腋。我一直安慰自己,他們只是感情好睡互,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布根竿。 她就那樣靜靜地躺著陵像,像睡著了一般。 火紅的嫁衣襯著肌膚如雪寇壳。 梳的紋絲不亂的頭發(fā)上醒颖,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機與錄音壳炎,去河邊找鬼泞歉。 笑死,一個胖子當(dāng)著我的面吹牛匿辩,可吹牛的內(nèi)容都是我干的腰耙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼铲球,長吁一口氣:“原來是場噩夢啊……” “哼挺庞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起稼病,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤选侨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后然走,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體援制,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年丰刊,在試婚紗的時候發(fā)現(xiàn)自己被綠了隘谣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片增拥。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡啄巧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掌栅,到底是詐尸還是另有隱情秩仆,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布猾封,位于F島的核電站澄耍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏晌缘。R本人自食惡果不足惜齐莲,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望磷箕。 院中可真熱鬧选酗,春花似錦、人聲如沸岳枷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至殿衰,卻和暖如春朱庆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背闷祥。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工娱颊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凯砍。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓维蒙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親果覆。 傳聞我的和親對象是個殘疾皇子颅痊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法局待,內(nèi)部類的語法斑响,繼承相關(guān)的語法,異常的語法钳榨,線程的語...
    子非魚_t_閱讀 31,631評論 18 399
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 29,386評論 8 265
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理舰罚,服務(wù)發(fā)現(xiàn),斷路器薛耻,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • 當(dāng)我寫下這篇文章的時候营罢,可能也會勾起大家無數(shù)的無數(shù)回憶。 當(dāng)我接觸夢幻西游的時候大概是05年那時候我上4年級饼齿,那天...
    肖拿包閱讀 614評論 1 4
  • 如此這般正襟危坐讓閱讀也有了儀式感 何處不是讀書地兒饲漾,坐擁整個大自然和圖書館,卻心系你缕溉,一本書 我可以給書中人物畫...
    伊呀學(xué)語閱讀 263評論 0 1