为何会说是一致性?那就须要从代码的原生讲起了;咱们都知道"=="是java的元素运算符,而equals是Object类其中的一个方法。java
“==”运算符在java中的做用应分为二种状况:编程
一、应用于基本数据类型【byte、short、int、long、float、double、char、boolean】,用于比较存储的值是否相等【值的内容】。数组
二、应用于引用数据类型【类(class)、接口(interface)、数组(array)】,用于比较所指向的对象地址是否相等。ide
“equals”是能用于比较引用类型,对于基本数据类型是没有equals的。学习
从源码入手:测试
* @param obj the reference object with which to compare. * @return {@code true} if this object is the same as the obj * argument; {@code false} otherwise. * @see #hashCode() * @see java.util.HashMap */ public boolean equals(Object obj) { return (this == obj); }
从以上源码能够看出equals底层也是先进行“==”比较,在进行别的操做。也印证了上面所说的“==”和equals是没有区别这句话;惟一的区别:是基本类型没有equals方法【没有继承Object类】,也就是说基本数据类型只能使用“==”进行比较两值是否相同。ui
在使用Java编程过程当中,使用“equals”进行比较的操做,应用最多的应该是Striing类中的equals()方法。【String中的equals是重写了Object中equals】this
老规矩直接从源码入手:spa
1 * @param anObject 2 * The object to compare this {@code String} against 3 * 4 * @return {@code true} if the given object represents a {@code String} 5 * equivalent to this string, {@code false} otherwise 6 * 7 * @see #compareTo(String) 8 * @see #equalsIgnoreCase(String) 9 */ 10 public boolean equals(Object anObject) { 11 if (this == anObject) { 12 return true; 13 } 14 if (anObject instanceof String) { 15 String anotherString = (String)anObject; 16 int n = value.length; 17 if (n == anotherString.value.length) { //判断两个比较内容长度是否相等 18 char v1[] = value; 19 char v2[] = anotherString.value; 20 int i = 0; 21 while (n-- != 0) { //比较内容是否一致 22 if (v1[i] != v2[i]) 23 return false; 24 i++; 25 } 26 return true; 27 } 28 } 29 return false; 30 }
从源码能够看出,重写的qulas仍是同样先判断“==”也就是两个对象所指向的地址是否相同,若相同则直接返回true,若不相同在进行下一步比较,在返回true,若两个都不知足则返回false。code
例如:
public static void main(String[] args) { String a = "Hello China"; String b = new String("Hello China"); String c = a; //值的传递,将a的内容传递给c //使用“==”和“equals”进行比较 //==对于引用数据类型比较的是指向的地址值是否相同 System.out.println(a == b);//false 地址不是指向同一个地方 System.out.println(a == c);//true 地址指向同一个地方 System.out.println(b == c);//false 地址不是指向同一个地方 //equals比较的是内容的值【先判断==,==不知足在判断值内容】 System.out.println(a.equals(b));//true 地址指向的内容是否一致 System.out.println(a.equals(c));//true 地址指向内容是都一致 System.out.println(b.equals(c));//true 地址指向内容是否一致 }
看了上面的例子,或许你仍是有点疑惑,为了更加方便理解咱们直接上图:
绿色的线是经过new出来的一个新的Striing对象,当new出一个先的对象时候,该对象会如今常量池中找是否存在该对象,若不存在则在常量池中建立一个字符串对象,而后将堆中该对象指向常量池中新建立的字符串对象。
附:需注意String中的intern()方法;【intern() 返回字符串对象的规范化表示形式。】
public static void main(String[] args) { String a = "Hello China"; String b = new String("Hello China"); b=b.intern(); System.out.println(a == b); //true System.out.println(a.equals(b)); //true }
intern()方法是检查字符串池是否存在,若是存在则直接返回true。若没有加 [b = b.intern()]则a==b应该返回false,加了intern()后当b在new一个新的对象前,会先到池里面查找是否存在,存在则直接指向就不在建立。故a==b也就指向了同一个地址,a==b就成立返回true。
一、例如:咱们新建一个Student类,new两个Student对象。这两个对象的姓名、年龄、性别相同,咱们就认为两个学生对象相等,但对象地址不必定相同。【未重写时两个对象地址不一样,重写后则相同】
1 public class Test { 2 public static void main(String[] args) { 3 4 //未重写时候的地址不一样 5 Student student1 = new Student("张三",20,"男"); 6 System.out.println(student1);//@266474c2 7 Student student2 = new Student("张三",20,"男"); 8 System.out.println(student2);//@6f94fa3e 9 System.out.println(student1.equals(student2));//未重写equals时为 false 10 11 12 Student student1 = new Student("张三",20,"男"); 13 System.out.println(student1);//@2c63a8ab 14 Student student2 = new Student("张三",20,"男"); 15 System.out.println(student2);//@2c63a8ab 16 System.out.println(student1.equals(student2));//重写equals后为 true 17 18 } 19 } 20 21 22 class Student{ 23 public String name; 24 public int age; 25 public String sex; 26 27 28 29 30 @Override 31 public int hashCode() { 32 final int prime = 31; 33 int result = 1; 34 result = prime * result + age; 35 result = prime * result + ((name == null) ? 0 : name.hashCode()); 36 result = prime * result + ((sex == null) ? 0 : sex.hashCode()); 37 return result; 38 } 39 40 42 43 @Override 44 public boolean equals(Object obj) { 45 if (this == obj) 46 return true; 47 if (obj == null) 48 return false; 49 if (getClass() != obj.getClass()) 50 return false; 51 Student other = (Student) obj; 52 if (age != other.age) 53 return false; 54 if (name == null) { 55 if (other.name != null) 56 return false; 57 } else if (!name.equals(other.name)) 58 return false; 59 if (sex == null) { 60 if (other.sex != null) 61 return false; 62 } else if (!sex.equals(other.sex)) 63 return false; 64 return true; 65 } 66 67 68 69 70 public Student(String name, int age, String sex) { 71 this.name = name; 72 this.age = age; 73 this.sex = sex; 74 } 75 }
重写equals大体是先判断是都是同一个对象,而后在依次比较对象内的name,age,sex值。【由于String类内部已经重写过equals因此直接使用equals就能够比较内容】
平常开发中,通常是要重写equals才符合实际生活状况!
可是随着重写equals又出现了新的问题,为何还要重写hashCode,不重写会怎么样!!!
hashcode方法是根据对象的地址转换以后返回的一个哈希值,使用hashcode方法,会返回一个哈希值,哈希值对数组的长度取余后会肯定一个存储的下标位置。不一样的哈希值取余以后的结果多是相同的,相同的时候就用equals方法判断是否为相同的对象,不一样则在链表中插入。若哈希值取余后不相同,则插入的位置也不一样,两个对象确定不相同。【结合图解】
小结:
通过上面描述后,在判断的时先根据hashcode进行的判断,相同的状况下再根据equals()方法进行判断。若是只重写了equals方法,而不重写hashcode的方法,会形成hashcode的值不一样,而equals()方法判断出来的结果为true。【地址不一样状况下也返回true】。而咱们须要的是两个对象在相同状况下在进行equals判断,所以重写hashCode是避免地址值不相同状况下也进行覆盖。
为了更进一步理解,这边使用HashMap的键值对来进行测试【HashMap的键不能重复,底层基于数组+链表+红黑树结构】==》链表长度大于8时候转换为红黑树
一、不重写hashCode()方法:
1 public class Test { 2 public static void main(String[] args) { 3 Student student1 = new Student("张三",20,"男"); 4 System.out.println("student1的hashCode值:"+student1.hashCode());// 5 Student student2 = new Student("张三",20,"男"); 6 System.out.println("student1的hashCode值:"+student2.hashCode());// 7 System.out.println("比较两个对象是否相等:"+student1.equals(student2));//未重写equals时为 false 8 HashMap<Student,Integer> studentMap = new HashMap(); 9 studentMap.put(student1,111); 10 studentMap.put(student2,222); 11 System.out.println("当前的map大小:"+studentMap.size());// 12 13 } 14 }
输出结果:
二、重写hashCode()方法:
输出结果:
很明显,在没有重写hashCoede()的时候,他们的hash值不一样,也就是存储在数组中的位置不一样,但由于重写了equals(),因此内容是一致的。但在不能出现重复键的Map中,他们仍是没有进行覆盖,显然这不符合实际逻辑。
在重写hashCode()以后,在进行map存储时候会先判断hashCode值是否相同,相同则指向存储数组中的同一个位置,在进行内容判断,内容相同才进行覆盖操做,故返回的size为1,若不相同则使用里链表方式,存放于后面。
小结:
一、不重写hashCode跟equals时候返回确定为false,由于原生Object的equals判断的是地址值【==】。
二、不重写hashCode,重写equals返回的是true,在进行存储时指向的位置下标不一样,但因为重写判断内容,因此使用equals判断内容时候会返回true。
三、重写hashCoe,不重写equals返回false,由于两个对象的地址相同了,但未重写比较内容,所以返回false【链表状况】。
四、重写了hashCode,重写了equals返回true,这个时候在map中指向的数组下标一致,内容也一致,就会产生覆盖。
【简单来讲:重写hashCode()是为了让二者在内存中指向同一个地方,而重写equals()是为了在指向同一个地方同时判断是都彻底一致】
如有不但之处,欢迎指正!一块儿学习!!!!