一、背景
這兩天,在網上逛的時候,發現了如下的一道面試題,感覺還有蠻有意思的,要是不仔細看還真容易掉到坑里面。第一眼看起來比較繞,所以比較難理解。最終我跳出了這個坑,也想把這個跳坑的經歷分享出來。題目如下 , 請問輸出的是什么?為什么?
/**
* @author hafiz.zhang
* @description: 一個奇怪的現象
* @date Created in 2018/7/2 22:44.
*/
public class Test {
public static void main(String[] args) {
String ermao = "a";
String bb = addStr(ermao);
System.out.println(ermao);
}
private static String addStr(String ermao) {
ermao = ermao + "b";
return ermao;
}
}
二、結果及原因
毫無疑問,這道題的答案是:a , 你答對了么?在講解原因之前,讓我們想了解一點基礎知識吧:
1. 值傳遞還是引用傳遞?
其實,Java官方並沒有給出值傳遞還是引用傳遞的概念。官方將Java參數傳遞類型分為傳遞基本類型參數和傳遞引用類型參數。當參數為基本類型(Java八大基本類型:int、short、float、double、long、boolean、byte、char)的時候,就是傳遞基本類型參數了,當參數為封裝類型(非基本類型,如Integer、Long、Boolean等)的時候,就是傳遞引用傳遞參數了。官方文檔地址:Java參數傳遞。
2. Java內存模型中的堆和棧
從Java的底層機制來說,基本類型的變量存放在棧里;封裝類型中,對象放在堆里,對象的引用放在棧里。Java在方法傳遞參數時,是將變量復制一份,然后傳入方法體去執行。
3. 答案是a的原因
因為String類型在傳遞過程中的步驟如下:
- 虛擬機在堆中開辟一塊內存,並存值”a”。
- 虛擬機在棧中分配給ermao一個內存,內存中存的是1中的堆地址。
- 虛擬機復制一份ermao,我們就叫ermao’好了,ermao和ermao’在棧中的內存不同,但此時存的值都是1的堆地址。
- 將ermao’傳入方法addStr中。
- 方法體在堆中開辟一塊新內存,並存值”ab”。
- 方法體將ermao’的值改變,存入5中新的堆內存地址。
- 方法結束,方法外打印ermao,由於ermao存的是1中分配的堆地址,所有打印結果還是”a”。
還不清晰?沒關系,那我們直接上個圖會不會來得更直觀:
4. Java到底有沒有引用傳遞
博友: 歸去來兮辭 說不是傳遞引用類型就是引用傳遞,Java中沒有引用傳遞。但其實 Java中並沒有定義值傳遞還是引用傳遞,非基本類型的參數傳遞就是傳遞引用類型參數,但String是個特例,String類型對象的值是不可變的,因為String類是通過final修飾的char[]數組來存放結果的。每次為String類型的變量重新賦值實際上都是新建了一個新的String實例,但是方法外部String類型變量沒有指向新的String實例,所以也就不會獲取到新的更改。這就導致了傳遞String類型參數時雖然是傳遞引用類型參數但是無法通過參數傳遞的方式改變其變量值。歡迎大家提出不同意見來相互討論學習哈~三、總結
通過本文我們就理解了Java在方法傳參的整個過程。其實還是上面那句比較重要的話Java在方法傳遞參數時,是將變量復制一份,然后傳入方法體去執行。給棒棒噠自己一波掌聲👏👏👏,點滴積累,方成大事~