一個對象引用的思考

一個有趣且令人困惑的代碼片段

Code A:

final ConcurrentHashMap<String, Ref> REFS_MAPS = new ConcurrentHashMap<String, Ref>();

public void put(String key) {
  Ref ref = new Ref(key, "1");
  ref = new Ref(key, "2");
  REFS_MAPS.put(key, ref);
}

public Ref get(String key) {
  return REFS_MAPS.get(key);
}

它有可能會得到"1"嗎?

錯誤的解釋

在多線程調度的情況下,相同的 key 多次同時調用 put 和 get 方法,從 REFS_MAPS 方法 get 時,正好 put 運行到 Ref ref = new Ref(key, "1"),所以就得到了“1”的值,如下所示:

image

這個解釋是錯誤的,不會得到“1”。

REFS_MAPS 的 hash Node 存儲指向 “ref” 對象的值,而不是對象引用。因此,當 ref 在 put() 方法時,ref 的 val 先指向堆中的“1”,后指向堆中的“2”,如下所示:

image

常見的困惑問題

將 put 方法改一下:

Code B:

public void put(String key) {
  Ref ref = new Ref(key, "1");
  REFS_MAPS.put(key, ref);
  ref = new Ref(key, "2");
}    

它有可能會得到"1"嗎?

一定會是“1”,雖然 ref 的指向堆中的“2”,但是 REFS_MAPS 的 hash Node 存儲指向 “ref” 對象的值還是“1”。

image

再將 put 方法改一下:

Code C:

public void put(String key) {
  Ref ref = new Ref(key, "1");
  REFS_MAPS.put(key, ref);
  ref.setValue("2");
}

它有可能會得到"1"嗎?

不會的得到“1”,因為 ref 和 REFS_MAPS 存儲的 “ref” 對象只指向的同一個值,當 ref 修改了值,REFS_MAPS 中 ref 的值也被修改了。

image

代碼背后真正的意義是什么?

我們知道,值傳遞(pass by value)是指在調用函數(shù)時將實際參數(shù)復制一份傳遞到函數(shù)中,引用傳遞(pass by reference)是指在調用函數(shù)時將實際參數(shù)的地址直接傳遞到函數(shù)中,而 Java 只有值傳遞。

在 Code B 中,ref = new Ref(key, "2") 會重新開辟一片內存空間,賦值給 ref,后面的任何修改都不會改變 Ref ref = new Ref(key, "1") 的內容,這里不是引用傳遞,如果是引用傳遞的話,REFS_MAPS 中的引用也應該會改變,但是實際上并沒有。

在 Code C 中,ref.setValue("2") 影響了 REFS_MAPS 中的值,因為這里是把 ref 的引用的地址復制了一份,傳遞給了 REFS_MAPS。所以,ref 其實是值傳遞,把 ref 對象引用的地址當做值傳遞給了 REFS_MAPS。

所以,值傳遞和引用傳遞的區(qū)別并不是傳遞的內容。而是實參到底有沒有被復制一份給形參。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容