上一篇文章有三个遗留问题,首先我们来详细讲解一下哈希值和内存地址
对象是存储在内存里面的,有唯一的内存地址,对象引用就是通过这个内存地址找到对象的。而哈希值就是通过哈希算法对对象的内存地址进行计算得到的一个整数值,可以理解为对象的数字标识。Object类提供了hashCode()方法,用来获取对象的哈希值。说到这里,大家应该知道不同对象的哈希值一定是不同的,因为它是根据对象唯一的内存地址计算出来的。那么如果想要让两个对象的哈希值相等,我们应该怎么做?
YES!就是覆盖Object类的hashCode()方法!
大多数Java核心类库中的API已经覆盖了hashCode方法,目的是根据对象内容计算出哈希值。口说无凭,我们以如下示例程序来讨论:
public class Test20 {
public static void main(String[] args){
//数组
int[] arr_1={1,2,3};
int[] arr_2={1,2,3};
int[] arr_3={1,2,4};
System.out.println("三个数组调用未覆盖的toString方法得到的结果分别为:"+arr_1+" "+arr_2+" "+arr_3);
System.out.println("三个数组调用未覆盖hashCode方法得到的哈希值分别为:"+arr_1.hashCode()+" "+arr_2.hashCode()+" "+arr_3.hashCode());
System.out.println("三个数组调用覆盖后hashCode方法得到的哈希值分别为:"+Arrays.hashCode(arr_1)+" "+Arrays.hashCode(arr_2)+" "+Arrays.hashCode(arr_3));
//字符串
String str_1="123";
String str_2="123";
String str_3="abc";
System.out.println("三个字符串调用覆盖后的toString方法得到的结果分别为:"+str_1+" "+str_2+" "+str_3);
System.out.println("三个字符串调用覆盖后hashCode方法得到的哈希值分别为:"+str_1.hashCode()+" "+str_2.hashCode()+" "+str_3.hashCode());
//包装类
Integer i1=10;
Integer i2=10;
Integer i3=11;
System.out.println("三个整数包装类调用覆盖后的toString方法得到的结果分别为:"+i1+" "+i2+" "+i3);
System.out.println("三个整数包装类调用覆盖后hashCode方法得到的哈希值分别为:"+i1.hashCode()+" "+i2.hashCode()+" "+i3.hashCode());
//普通类
Test20 t=new Test20();
System.out.println(t);
System.out.println(t.hashCode());
}
}
程序运行结果:
首先看数组。数组是一个比较特殊的对象,因为它并不是由特定类型声明的,所以数组引用调用的只能是Object类的原始方法。所以通过System.out.println()打印数组就会调用原始的Object类toString()方法,Object类的toString方法的返回值是:对象类型+@+对象的十六进制哈希值!我们可以看到这三个数组的十六进制哈希值是不同的,验证了内存地址的唯一性。同理,数组引用调用的hashCode()方法也是原始的Object类hashCode()方法,同理三个数组的哈希值也是不同的。
那覆盖后的hashCode()和toString()方法去哪儿了?它们都是Arrays类的静态方法!上一篇文章已经演示了Arrays的toString()方法可以打印出数组内容,实际上它就是覆盖了原始的Object类的toString()方法。Arrays类中的hashCode()方法也覆盖了原始的hashCode()方法,目的是根据数组内容计算出哈希值,所以两个数组[1,2,3]的哈希值是相同的!
字符串相比数组的优势就是,它是由String类型声明的,所以字符串引用可以直接调用String类中覆盖版本的toString()和hashCode()方法。很明显,String类的hashCode()方法也是根据字符串内容计算出哈希值,所以两个字符串“123”的哈希值是相同的。包装类型同理。
除了这些Java核心类库提供的API,开发人员自己编写的类最好覆盖toString()方法和hashCode()方法。
还要注意一点:两个不同对象,调用Object类的hashCode()方法返回的哈希值一定不同(因为对象内存地址唯一),但是调用自己覆盖后的hashCode()方法返回的哈希值是有可能相同的!这是由我们自己定义的算法本身的原因导致的。