结论:不是,hash code 只是Java 用来标识对象的,你可以形象的把它视为对象的身份证号码。
- 从对象.hashCode() 方法着手
官方API 的说法:
那具体实现是怎么样的?
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined
* by class {@code Object} does return distinct integers for
* distinct objects. (The hashCode may or may not be implemented
* as some function of an object's memory address at some point
* in time.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
@HotSpotIntrinsicCandidate
public native int hashCode();
结果它是被native 关键字修饰,也就是说具体的实现不是由Java 来实现的,Java 只负责调用!
再有就是注释说:‘As much as is reasonably practical… some function of an object’s memory address at some point in time)’,翻译一下,从最合乎逻辑、切合实际的角度来看,在不同类定义的hashCode() 方法就是应该给不同的对象返回不同的hash code。(Hash code 也许是或者不是通过对象的内存地址来实现。)
其实,有大神自己用Open JDK 去查找hashCode() 的底层实现,可以参照以下文章:
Java Object.hashCode()返回的是对象内存地址?
文章中得出的结论:
Hash code 有5中不同的生成策略:
1) 返回一个Park-Miller伪随机数生成器生成的随机数。
2) 返回将对象的内存地址做移位运算后与一个随机数进行异或得到结果。
3) 返回1。
4) 返回一个自增序列的当前值。
5) 返回当前对象的内存地址。
6) 返回由当前线程有关的一个随机数和三个确定值,经Marsaglia’s xorshift scheme随机数算法得到的一个随机数。
所以,跟内存相关的生成方式只有1种,只是JVM 默认选择6)。
- 通过设置JVM 启动参数来输出对象的内存地址
是的,我们可以设置JVM 的启动参数来输出对象的内存地址,但是我们需要通过什么来查看呢?没错,就是本篇文章的主题 —— hash code.
1) 参数 & 值:
1. -XX:+UnlockExperimentalVMOptions
2. -XX:hashCode=4
注:4代表上述引用文章中,hash code 生成策略的第5项策略。
2) 在IDEA 中的测试:
1. 设置JVM 的启动参数,如1)。
Run -> Edit Configurations -> VM Options
如下图:
2. 写DEMO 代码。
注:本人安装过JOL (Java Object Layout) 插件,所以输出的结果包含通过插件的输出的结果。
[JetBrains: JOL Java Object Layout(https://plugins.jetbrains.com/plugin/10953-jol-java-object-layout)
如下:
import java.util.HashSet;
import java.util.Set;
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.info.GraphLayout;
public class HashCodeDemo {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public static void main(String[] args) {
HashCodeDemo a = new HashCodeDemo();
HashCodeDemo b = new HashCodeDemo();
a.setI(1);
b.setI(1);
Set<HashCodeDemo> set = new HashSet<HashCodeDemo>();
set.add(a);
set.add(b);
System.out.println(System.getProperty("java.vm.name"));
System.out.println(System.getProperty("java.vm.version"));
System.out.println("a.hashCode():" + a.hashCode());
System.out.println("a.hashCode():0x" + Integer.toHexString(a.hashCode()));
System.out.println(ClassLayout.parseInstance(a).toPrintable());
System.out.println(GraphLayout.parseInstance(a).toPrintable());
System.out.println("size : " + GraphLayout.parseInstance(a).totalSize());
System.out.println("----------------------");
System.out.println("b.hashCode():" + b.hashCode());
System.out.println("b.hashCode():0x" + Integer.toHexString(b.hashCode()));
System.out.println(ClassLayout.parseInstance(b).toPrintable());
System.out.println(GraphLayout.parseInstance(b).toPrintable());
System.out.println("size : " + GraphLayout.parseInstance(b).totalSize());
}
}
结果:
从测试结果来看,我们不难发现,对象a 和b 的hash code 就是他们的自身的内存地址,可以从对象头、ADDRESS 列的值观察。
当我们将JVM 的启动参数复原,即删除VM options 里的值:
关于Mark Word,本人也还在学习中,只是知道里面存有hash code,并且用来表示对象的线程锁状态。学习完毕后,会分享学习心得。
本人在看以下文章:
Java对象结构述
好好学习,天天向上!
———————————————————————————
Written by Freeman
Date: Feb. 21st, 2020.