Object.toString()打印「地址」的原理

Object.toString()打印「地址」的原理

@(java)java

首先,打印的毫不是地址

public native int hashCode();
public boolean equals(Object obj) {
        return (this == obj);
}
public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

能夠發現咱們打印的是調用對象的對應Class對象的getName()方法返回值和hashcode()方法的16進製表示。程序員

Object.hashCode()是什麼?

native方法指原生態方法,它調用的不是java語言,而是其餘語言,好比C語言。
閱讀hashCode的spec,咱們發現根本沒有說起其實現方式編程

/ **
返回對象的哈希碼值。這種方法是
支持哈希表的好處,例如那些提供的哈希表
* {@link java.util.HashMap}。
* {hashCode}的通常合約是:
* 每次在同一個對象中調用屢次
執行Java應用程序,即{hashCode}方法
必須始終返回相同的整數,不提供任何信息
用於在對象上進行修改的{@code equals}比較。
這個整數不須要從一個執行中保持一致
應用程序到另外一個執行相同的應用程序。
若是根據equals(Object)}兩個對象相等
方法,而後在每一個方法上調用{@code hashCode}方法
這兩個對象必須產生相同的整數結果。
* 若是兩個對象不相等,則不須要
根據{@link java.lang.Object#equals(java.lang.Object)}
方法,而後在每一個方法上調用{@code hashCode}方法
兩個對象必須產生不一樣的整數結果。可是,那
程序員應該知道產生不一樣的整數結果
用於不相等的對象能夠提升散列表的性能。
儘量合理實用,由。定義的hashCode方法
* class {@code Object}確實爲不一樣的返回不一樣的整數
對象。 (這一般經過轉換內部來實現
對象的地址轉換爲整數,可是這個實現
技術不是必需的
Java™編程語言)。
* @返回此對象的哈希碼值。
* @see java.lang.Object#equals(java.lang.Object)
參見java.lang.System#identityHashCode
/api

再參考最近幾個版本jdk的API簡介中對hashcode實現的介紹併發

  • jdk8:This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the Java™ programming language.(ps:這句話不是很合適,java有垃圾回收機制,其爲了整理內存會執行「堆壓縮」,將被佔用的內存集中在一塊兒減小內存碎片,故內部地址也可能被修改,使用內部地址來生成hashcode不是很好)
  • jdk9/10:The hashCode may or may not be implemented as some function of an object's memory address at some point in time.

從java源碼中們不能得知hashcode的具體實現方式,只知道其spec。api描述也很是曖昧。
咱們能夠推測的是jvm的設計目標包括跨平臺適用,因此其hashcode的實現應該是在各個平臺上不一樣的,或者與平臺無關。jvm

關於Object.hashCode()原理的stack overflow高票答案

更深刻的理解應該是困難的,因此下面我整理了stack overflow的高票答案,僅供參考。請注意它的時效性,雖然我已經選取最新的,可是仍是可能有出入,建議做爲閱讀材料來處理,畢竟hashCode到底如何實現和咱們使用java無關。編程語言

在HotSpot JVM默認狀況下,第一次調用非超載Object.hashCode或System.identityHashCode隨機數時會生成並存儲在對象頭中。隨後調用Object.hashCode或System.identityHashCode僅從頭>中提取此值。默認狀況下,它與對象內容或對象位置沒有什麼共同之處,只是隨機數。此行爲由-XX:hashCode=n具備如下可能值的HotSpot JVM選項控制:ide

  • 0:使用全局隨機生成器。這是Java 7中的默認設置。它具備的缺點是,來自多個線程的併發調用可能會致使爭用條件,從而致使爲不一樣對象生成相同的hashCode。並且在高度併發環境中,由>於爭用(使用來自不一樣CPU內核的相同內存區域),可能會出現延遲。
  • 1:使用對象指針與一些在「世界中止」事件中更改的隨機值混合,所以在中止世界事件(如垃圾收集)生成的hashCode之間是穩定的(用於測試/調試目的)
  • 2:始終使用1(用於測試/調試目的)
  • 3:使用自動增長數字(爲了測試/調試目的,還使用全局計數器,所以爭用和競爭條件是可能的)
  • 4:若是須要,使用修剪爲32位的對象指針(用於測試/調試目的)
  • 5:使用一些沒有先前缺點的線程局部xor-shift隨機生成器。這是Java 8中的默認設置。
    請注意,即便您設置了-XX:hashCode=4,hashCode也不會始終指向對象地址。對象可能會稍後移動,但hashCode將保持不變。另外,對象地址分佈不均勻(若是您的應用程序使用的內存不足,大多數對象將彼此靠近),所以若是使用此選項,最終可能會出現不平衡的哈希表。
    若是hashCode()沒有被覆蓋,什麼是對象的哈希碼?

咱們從這個回答能發現,7 8兩代的默認實現形式都去除了和地址的關聯。猜想這樣的實現形式能更好的知足跨平臺的設計目標。性能

咱們應該如何使用原生hashCode()

核心觀點是hashCode只能被用來判斷不相同,不能用來判斷相同,Objcet.hashCode()並無由於是Object的hashCode而有任何特殊之處。
理由很直白:不管是之內部地址生成隨機數,仍是直接使用隨機數生成器生成隨機數,其長度都被截取爲32位,必定存在重複。咱們不能認爲hashCode相同則就是同一個對象。hashCode應該被用來優化檢索,由於若是hashCode不一樣則必定不是同一對象。測試

相關文章
相關標籤/搜索