1)"=="运算符是比较两个变量的值是否相等。也就是说,该运算符用于比较变量对应的内存中所存储的值是否相等,要比较两个基础类型的数据或两个引用变量是否相等,只能使用"=="运算符。
具体而言,如果两个变量是基础类型,可以直接使用"=="运算符判断对应的值是否相等。如果一个变量指向的是对象(引用类型),那么,此时涉及两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如对于语句String s = new String(),变量s占用一块存储空间,而new String()占用另一块存储空间,此时变量s所对应对应内存中的存储数值就是对象占用的那块内存的首地址。对于指向类型的变量,如果判断两个变量是否指向一个相同的对象,即要比较这两个变量对应的内存中的数值是否相同(这两个变量是否指向同一个变量),这时可以通过"=="运算符来进行比较,但是如果要比较这两个对象的内容是否相等,使用"=="就无法实现了。
2)equals是Object对象中的方法,每个Java类都继承于Object,所以每一个对象都具有equals方法,Object中的equals方法直接使用"=="运算符来比较两个对象,所以在没有覆盖equals方法的情况下,equals与"=="结果是一样的,都是比较对象的引用。
相比"=="运算符,equals方法的特殊之处就是可以被覆盖,所以可以通过覆盖,让该方法比较的不是引用,而是我们需要的数据内容,,例如,String类中的equals方法是用于比较两个独立的对象的内容是否相同,即堆中的内容是否相等,以下面的代码为例:
String s1= new String("hello");
String s2= new String("hello");
两个new语句创建了两个对象,然后分别用s1和s2指向一个对象,这是两个不同的对象,他们的首地址是不相同的,即s1和s2存储的数值是不相同的,所以表达式s1==s2将返回false,而这两个对象的内容是相同的,所以equals方法将返回true。
通过以上例子可以说明,如果一个类没有复写euqals方法,那么它就从Object中继承equals方法,并且默认使用"=="运算符来进行比较,也就是在比较两个对象的引用是否相同,因此equals和==会得到相同的结果。若比较两个独立的对象,则总会返回false。如果编写的类希望能够比较该类创建的两个实例对象的内容是否相等,那么必须覆盖equals方法。
3)hashCode()方法是从Object类中继承的,它也鉴定两个对象是否相等,Object类中的hashCode返回对象在内存中的地址的int值,所以在不重写hashcode方法时,任何对象的hashcode都是不相同的。
虽然equals也是比较两个对象是否相等的,但是它和hashCode方法是有区别的。一般来讲,equals方法是给用户来使用的,如果要判断两个对象是否相等,需要覆盖equals方法,然后在代码中调用,这样就可以判断它们是否相等了。对于hashCode方法用户一般是不会调用它的,例如在hashMap中,由于key是不重复的,它在判断key是否重复的时候就调用了hashCode方法,而且也用类equals方法。此处不可重复,指的是,equals和hashCode方法中的一个不相同就可以了。所以hashCode相等于一个对象的编码表,就好像文件中的md5,它与equals方法的区别在于它的返回值是int类型,比较起来不直观。
一般在覆盖equals方法的同时也同样需要覆盖hashCode方法,否则,就会违反Object.hashCode的通用规则约定,从而导致无法与所有基于散列表的集合类(hashMap hashSet hashTable)结合在一起正常使用。
hashCode方法和equals方法的关系如下:如果x.equals的返回值true,即两个对象通过equals方法比较是相同的,那么调用二者的hashCode方法将返回相同的int值。如果x.equals的返回值false,即两个对象通过quals方法比较的结果是不相同的,那么x,y的hashCode的返回值可能相等也可能不相等。反之,hashCode的返回值不相等,一定可以推出equals方法的返回值是不相等的,而hashCode的返回值相等,equals的返回值可能相等也可能不相等。