日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

你好,我是 阿遠。

一般面試我都會問一兩道很基礎的題目,來考察候選人的“地基”是否扎實,有些是操作系統層面的,有些是 JAVA 語言方面的,還有些…

最近我都拿一道 Java 語言基礎題來考察候選人:

不用反射,能否實現一個方法,調換兩個 String 對象的實際值?

String yesA = "a";
String yesB = "b";
//能否實現這個 swap 方法
// 讓yesA=b,yesB=a?
swap(yesA, yesB);

別小看這道題,其實可以考察好幾個點:

1.明確 yesA 和 yesB 是啥

2.Java 只有值傳遞

3.String 是不可變類

4.字符串常量池

5.intern 的理解

6.JVM內存的劃分與改變

基于上面這幾個點,其實還能發散出很多面試題,不過今天咱們這篇文章就不發散了,好好消化上面這幾個點就可以了。

我們需要明確答案:實現不了這個方法。

按照題意,我相信你很容易能寫出以下的 swap 方法實現:

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

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

最近我面了12個人,發現這個JAVA基礎題都答得不好

 

也就是說變量 yesA 存儲的只是一個引用,通過它能找到 a 這個對象,所以表現出來好像 yesA 就是 a,實際你可以理解 yesA 存儲是一個“地址”,Java 通過這個地址就找到對象 a。

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

然后,我們都聽過 Java 中只有值傳遞,也就是調用方法的時候 Java 會把變量 yesA 的值傳遞到方法上定義的 yesA(同理 yesB 也是一樣),只是值傳遞。

根據上面我們已經知道 yesA 存儲的是引用,所以我們得知,swap方法 里面的 yesA 和 yesB 拿到的是引用。

最近我面了12個人,發現這個JAVA基礎題都答得不好

 

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

最近我面了12個人,發現這個JAVA基礎題都答得不好

 

請問,swap 里的跟我外面的 yesA 和 yesB 有關系嗎?顯然,沒有關系。

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

不信的話,我們看下代碼執行的結果:

最近我面了12個人,發現這個JAVA基礎題都答得不好

 

現在,我們明確了,Java 只有值傳遞。

看到這,可能會有同學疑惑,那 int 呢,int 不是對象呀,沒引用啊,其實一樣的,記住Java 只有值傳遞。

我們跑一下就知道了:

最近我面了12個人,發現這個JAVA基礎題都答得不好

 

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

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

調用 swap 時候,傳遞的值是 1 和 2,你可以理解為拷貝了一個副本過去。

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

顯然沒有。

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

不會。

記住,Java 只有值傳遞。

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

那什么是不可變類呢?

我在之前的文章說過,這邊我引用一下:

不可變類指的是無法修改對象的值,比如 String 就是典型的不可變類,當你創建一個 String 對象之后,這個對象就無法被修改。

因為無法被修改,所以像執行s += “a”; 這樣的方法,其實返回的是一個新建的 String 對象,老的 s 指向的對象不會發生變化,只是 s 的引用指向了新的對象而已。

看下面這幅圖應該就很清晰了:

最近我面了12個人,發現這個JAVA基礎題都答得不好

 

如圖所示,每次其實都是新建了一個對象返回其引用,并不會修改以前的對象值,所以我們常說不要在字符串拼接頻繁的場景不要使用 + 來拼接,因為這樣會頻繁的創建對象,影響性能。

而一般你說出 String 是不可變類的時候,面試官一般都會追問:

不可變類有什么好處?

來,我也為你準備好答案了:

最主要的好處就是安全,因為知曉這個對象不可能會被修改,在多線程環境下也是線程安全的(你想想看,你引用的對象是一個不可變的值,那么誰都無法修改它,那它永遠就是不變的,別的線程也休息動它分毫,你可以放心大膽的用)。

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

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

例如執行了 String yesA = "a" 這行代碼,我們現在知道 yesA 是一個引用指向了堆中的對象 a,再具體點其實指向的是堆里面的字符串常量池里的對象 a。

如果字符串常量池已經有了 a,那么直接返回其引用,如果沒有 a,則會創建 a 對象,然后返回其引用。

這種叫以字面量的形式創建字符串。

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

String yesA = new String("a")

這種方式又不太一樣,首先這里出現了字面量 “a”,所以會判斷字符串常量池里面是否有 a,如果沒有 a 則創建一個 a,然后會在堆內存里面創建一個對象 a,返回堆內存對象 a 的引用,也就是說返回的不是字符串常量池里面的 a

我們從下面的實驗就能驗證上面的說法,用字面量創建返回的引用都是一樣的,new String 則不一樣

最近我面了12個人,發現這個JAVA基礎題都答得不好

 

至此,你應該已經清晰字面量創建字符串和new String創建字符串的區別了。

講到這,經常還會伴隨一個面試題,也就是 intern

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

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個人,發現這個JAVA基礎題都答得不好

 

第一個輸出是 false 應該沒什么疑義,一個是字符串常量的引用,一個是堆內的(實際上還是有門道的,看下面)。

第二個輸出是 true 主要是因為這個 intern 方法。

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

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

你以為這樣就結束了嗎?

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

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 之后,結果就變了:

最近我面了12個人,發現這個JAVA基礎題都答得不好

 

是不是有點懵?奇了怪了,按照上面的邏輯不應該啊。

實際上,我最初畫字符串常量池的時候,就將其畫在堆內,也一直說字符串常量池在堆內,這是因為我是站在 JDK 1.8 的角度來說事兒的。

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

最近我面了12個人,發現這個JAVA基礎題都答得不好

 

這區域的改變就導致了 intern 的返回值有變化了。

在這個認知前提下,我們再來看修改順序后的代碼具體是如何執行的:

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

此時,堆內會新建一個 aaabbb 對象(對于 aaa 和 bbb 的對象討論忽略),字符串常量池里不會創建,因為并沒有出現 aaabbb 這個字面量。

2.String yesC = yesB.intern();

此時,會在字符串常量池內部創建 aaabbb 對象?

關鍵點來了。

在 JDK 1.6 時,字符串常量池是放置在永久代的,所以必須新建一個對象放在常量池中。

但 JDK 1.7 之后字符串常量池是放在堆內的,而堆里已經有了剛才 new 過的 aaabbb 對象,所以沒必要浪費資源,不用再存儲一份對象,直接存儲堆中的引用即可,所以 yesC 這個常量存儲的引用和 yesB 一樣。

3.String yesA = "aaabbb";

同理,在 1.7 中 yesA 得到的引用與 yesC 和 yesB 一致,都指向堆內的 aaabbb 對象。

4.最終的答案都是 true

現在我們知曉了,在 1.7 之后,如果堆內已經存在某個字符串對象的話,再調用 intern 此時不會在字符串常量池內新建對象,而是直接保存這個引用然后返回。

你看這面試題坑不坑,你還得站在不同的 JDK 版本來回答,不然就是錯的,但是面試官并不會提醒你版本的情況。

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

最后

你看,就這么一個小小的基礎題就可以引出這么多話題,還能延伸到 JVM 內存的劃分等等。

這其實很考驗基礎,也能看出來一個人學習的知識是否串起來,因為這些知識都是有關聯性的,給你一個點,就能擴散成面,這樣的知識才成體系。

歡迎關注我~

分享到:
標簽:基礎 JAVA
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定