亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

你好,我是 阿遠(yuǎn)。

一般面試我都會(huì)問(wèn)一兩道很基礎(chǔ)的題目,來(lái)考察候選人的“地基”是否扎實(shí),有些是操作系統(tǒng)層面的,有些是 JAVA 語(yǔ)言方面的,還有些…

最近我都拿一道 Java 語(yǔ)言基礎(chǔ)題來(lái)考察候選人:

不用反射,能否實(shí)現(xiàn)一個(gè)方法,調(diào)換兩個(gè) String 對(duì)象的實(shí)際值?

String yesA = "a";
String yesB = "b";
//能否實(shí)現(xiàn)這個(gè) swap 方法
// 讓yesA=b,yesB=a?
swap(yesA, yesB);

別小看這道題,其實(shí)可以考察好幾個(gè)點(diǎn):

1.明確 yesA 和 yesB 是啥

2.Java 只有值傳遞

3.String 是不可變類

4.字符串常量池

5.intern 的理解

6.JVM內(nèi)存的劃分與改變

基于上面這幾個(gè)點(diǎn),其實(shí)還能發(fā)散出很多面試題,不過(guò)今天咱們這篇文章就不發(fā)散了,好好消化上面這幾個(gè)點(diǎn)就可以了。

我們需要明確答案:實(shí)現(xiàn)不了這個(gè)方法。

按照題意,我相信你很容易能寫出以下的 swap 方法實(shí)現(xiàn):

void swap(String yesA, String yesB){
String temp = yesA;
yesA = yesB;
yesB = temp;
}

首先,我們要知道 String yesA = "a"; 這行代碼返回的 yesA 代表的是一個(gè)引用,這個(gè)引用指向堆里面的對(duì)象 a。

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

也就是說(shuō)變量 yesA 存儲(chǔ)的只是一個(gè)引用,通過(guò)它能找到 a 這個(gè)對(duì)象,所以表現(xiàn)出來(lái)好像 yesA 就是 a,實(shí)際你可以理解 yesA 存儲(chǔ)是一個(gè)“地址”,Java 通過(guò)這個(gè)地址就找到對(duì)象 a。

因此,我們知道了, yesA 存儲(chǔ)的值不是 a,是引用(同理,yesB也一樣)。

然后,我們都聽(tīng)過(guò) Java 中只有值傳遞,也就是調(diào)用方法的時(shí)候 Java 會(huì)把變量 yesA 的值傳遞到方法上定義的 yesA(同理 yesB 也是一樣),只是值傳遞。

根據(jù)上面我們已經(jīng)知道 yesA 存儲(chǔ)的是引用,所以我們得知,swap方法 里面的 yesA 和 yesB 拿到的是引用。

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

然后調(diào)用了 swap 方法,調(diào)換了 yesA 和 yesB 的值(也就是它的引用)

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

請(qǐng)問(wèn),swap 里的跟我外面的 yesA 和 yesB 有關(guān)系嗎?顯然,沒(méi)有關(guān)系。

因此最終外面的 yesA 指向的還是 a,yesB 指向的還是 b。

不信的話,我們看下代碼執(zhí)行的結(jié)果:

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

現(xiàn)在,我們明確了,Java 只有值傳遞

看到這,可能會(huì)有同學(xué)疑惑,那 int 呢,int 不是對(duì)象呀,沒(méi)引用啊,其實(shí)一樣的,記住Java 只有值傳遞。

我們跑一下就知道了:

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

很顯然, int 也無(wú)法交換成功,道理是一樣的。

外面的 yesA 和 yesB,存儲(chǔ)的值是 1 和 2(這里不是引用了,堆里也沒(méi)有對(duì)象,棧上直接分配值)。

調(diào)用 swap 時(shí)候,傳遞的值是 1 和 2,你可以理解為拷貝了一個(gè)副本過(guò)去。

所以 swap 里的 yesA 和 yesB 實(shí)際上是副本,它的值也是 1 和 2,然后副本之間進(jìn)行了交換,那跟正主有關(guān)系嗎?

顯然沒(méi)有。

像科幻電影里面有克隆人,克隆人死了,正主會(huì)死嗎?

不會(huì)。

記住,Java 只有值傳遞

再回到這個(gè)面試題,你需要知道 String 是不可變類。

那什么是不可變類呢?

我在之前的文章說(shuō)過(guò),這邊我引用一下:

不可變類指的是無(wú)法修改對(duì)象的值,比如 String 就是典型的不可變類,當(dāng)你創(chuàng)建一個(gè) String 對(duì)象之后,這個(gè)對(duì)象就無(wú)法被修改。

因?yàn)闊o(wú)法被修改,所以像執(zhí)行s += “a”; 這樣的方法,其實(shí)返回的是一個(gè)新建的 String 對(duì)象,老的 s 指向的對(duì)象不會(huì)發(fā)生變化,只是 s 的引用指向了新的對(duì)象而已。

看下面這幅圖應(yīng)該就很清晰了:

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

如圖所示,每次其實(shí)都是新建了一個(gè)對(duì)象返回其引用,并不會(huì)修改以前的對(duì)象值,所以我們常說(shuō)不要在字符串拼接頻繁的場(chǎng)景不要使用 + 來(lái)拼接,因?yàn)檫@樣會(huì)頻繁的創(chuàng)建對(duì)象,影響性能。

而一般你說(shuō)出 String 是不可變類的時(shí)候,面試官一般都會(huì)追問(wèn):

不可變類有什么好處?

來(lái),我也為你準(zhǔn)備好答案了:

最主要的好處就是安全,因?yàn)橹獣赃@個(gè)對(duì)象不可能會(huì)被修改,在多線程環(huán)境下也是線程安全的(你想想看,你引用的對(duì)象是一個(gè)不可變的值,那么誰(shuí)都無(wú)法修改它,那它永遠(yuǎn)就是不變的,別的線程也休息動(dòng)它分毫,你可以放心大膽的用)。

然后,配合常量池可以節(jié)省內(nèi)存空間,且獲取效率也更高(如果常量池里面已經(jīng)有這個(gè)字符串對(duì)象了,就不需要新建,直接返回即可)。

所以這里就提到 字符串常量池了。

例如執(zhí)行了 String yesA = "a" 這行代碼,我們現(xiàn)在知道 yesA 是一個(gè)引用指向了堆中的對(duì)象 a,再具體點(diǎn)其實(shí)指向的是堆里面的字符串常量池里的對(duì)象 a。

如果字符串常量池已經(jīng)有了 a,那么直接返回其引用,如果沒(méi)有 a,則會(huì)創(chuàng)建 a 對(duì)象,然后返回其引用。

這種叫以字面量的形式創(chuàng)建字符串。

還有一種是直接 new String,例如:

String yesA = new String("a")

這種方式又不太一樣,首先這里出現(xiàn)了字面量 “a”,所以會(huì)判斷字符串常量池里面是否有 a,如果沒(méi)有 a 則創(chuàng)建一個(gè) a,然后會(huì)在堆內(nèi)存里面創(chuàng)建一個(gè)對(duì)象 a,返回堆內(nèi)存對(duì)象 a 的引用,也就是說(shuō)返回的不是字符串常量池里面的 a

我們從下面的實(shí)驗(yàn)就能驗(yàn)證上面的說(shuō)法,用字面量創(chuàng)建返回的引用都是一樣的,new String 則不一樣

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

至此,你應(yīng)該已經(jīng)清晰字面量創(chuàng)建字符串和new String創(chuàng)建字符串的區(qū)別了。

講到這,經(jīng)常還會(huì)伴隨一個(gè)面試題,也就是 intern

以下代碼你覺(jué)得輸出的值各是啥呢?你可以先思考一下

String yesA = "aaabbb";
String yesB = new String("aaa") + new String("bbb");
String yesC = yesB.intern();
System.out.println(yesA == yesB);
System.out.println(yesA == yesC);

好了,公布答案:

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

第一個(gè)輸出是 false 應(yīng)該沒(méi)什么疑義,一個(gè)是字符串常量的引用,一個(gè)是堆內(nèi)的(實(shí)際上還是有門道的,看下面)。

第二個(gè)輸出是 true 主要是因?yàn)檫@個(gè) intern 方法。

intern 方法的作用是,判斷下 yesB 引用指向的值在字符串常量里面是否有,如果沒(méi)有就在字符串常量池里面新建一個(gè) aaabbb 對(duì)象,返回其引用,如果有則直接返回引用。

在我們的例子里,首先通過(guò)字面量定義了 yesA ,因此當(dāng)定義 yesC 的時(shí)候,字符串常量池里面已經(jīng)有 aaabbb 對(duì)象(用equals()方法確定是否有對(duì)象),所以直接返回常量池里面的引用,因此 yesA == yesC

你以為這樣就結(jié)束了嗎?

我們把上面代碼的順序換一下:

String yesB = new String("aaa") + new String("bbb");
String yesC = yesB.intern();
String yesA = "aaabbb"; // 這里換了
System.out.println(yesA == yesB);
System.out.println(yesA == yesC);

把 yesA 的定義放到 yesC 之后,結(jié)果就變了:

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

是不是有點(diǎn)懵?奇了怪了,按照上面的邏輯不應(yīng)該啊。

實(shí)際上,我最初畫字符串常量池的時(shí)候,就將其畫在堆內(nèi),也一直說(shuō)字符串常量池在堆內(nèi),這是因?yàn)槲沂钦驹?JDK 1.8 的角度來(lái)說(shuō)事兒的。

在 JDK 1.6 的時(shí)候字符串常量池是放在永久代的,而 JDK 1.7 及之后就移到了堆中。

最近我面了12個(gè)人,發(fā)現(xiàn)這個(gè)JAVA基礎(chǔ)題都答得不好

 

這區(qū)域的改變就導(dǎo)致了 intern 的返回值有變化了。

在這個(gè)認(rèn)知前提下,我們?cè)賮?lái)看修改順序后的代碼具體是如何執(zhí)行的:

1.String yesB = new String("aaa") + new String("bbb");

此時(shí),堆內(nèi)會(huì)新建一個(gè) aaabbb 對(duì)象(對(duì)于 aaa 和 bbb 的對(duì)象討論忽略),字符串常量池里不會(huì)創(chuàng)建,因?yàn)椴](méi)有出現(xiàn) aaabbb 這個(gè)字面量。

2.String yesC = yesB.intern();

此時(shí),會(huì)在字符串常量池內(nèi)部創(chuàng)建 aaabbb 對(duì)象?

關(guān)鍵點(diǎn)來(lái)了。

在 JDK 1.6 時(shí),字符串常量池是放置在永久代的,所以必須新建一個(gè)對(duì)象放在常量池中。

但 JDK 1.7 之后字符串常量池是放在堆內(nèi)的,而堆里已經(jīng)有了剛才 new 過(guò)的 aaabbb 對(duì)象,所以沒(méi)必要浪費(fèi)資源,不用再存儲(chǔ)一份對(duì)象,直接存儲(chǔ)堆中的引用即可,所以 yesC 這個(gè)常量存儲(chǔ)的引用和 yesB 一樣。

3.String yesA = "aaabbb";

同理,在 1.7 中 yesA 得到的引用與 yesC 和 yesB 一致,都指向堆內(nèi)的 aaabbb 對(duì)象。

4.最終的答案都是 true

現(xiàn)在我們知曉了,在 1.7 之后,如果堆內(nèi)已經(jīng)存在某個(gè)字符串對(duì)象的話,再調(diào)用 intern 此時(shí)不會(huì)在字符串常量池內(nèi)新建對(duì)象,而是直接保存這個(gè)引用然后返回。

你看這面試題坑不坑,你還得站在不同的 JDK 版本來(lái)回答,不然就是錯(cuò)的,但是面試官并不會(huì)提醒你版本的情況。

其實(shí)很多面試題都是這樣的,看似拋給你一個(gè)問(wèn)題,你好像能直接回答,如果你直接回答,那就錯(cuò)了,你需要先聲明一個(gè)前提,然后再回答,這樣才正確。

最后

你看,就這么一個(gè)小小的基礎(chǔ)題就可以引出這么多話題,還能延伸到 JVM 內(nèi)存的劃分等等。

這其實(shí)很考驗(yàn)基礎(chǔ),也能看出來(lái)一個(gè)人學(xué)習(xí)的知識(shí)是否串起來(lái),因?yàn)檫@些知識(shí)都是有關(guān)聯(lián)性的,給你一個(gè)點(diǎn),就能擴(kuò)散成面,這樣的知識(shí)才成體系。

歡迎關(guān)注我~

分享到:
標(biāo)簽:基礎(chǔ) JAVA
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定