Java ==,equals,hashCode,解析(适合初学者阅读)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java ==,equals,hashCode,解析(适合初学者阅读)相关的知识,希望对你有一定的参考价值。

1.对于equals()和hashCade()方法他们都是位于Java.lang.object类下的方法,又因为所有的类都继承于objct类,所以所有的类都可以使用这两个方法.

2.对于equals()方法,首先知道他是用于比较的,这里我吧源码打出来(注意:我这打出的源码是Object类中的equals()方法的源码)

  public boolean equals(Object obj){

  return (this==obj);

  }

附录:this关键字我在这里进行简单的说明一下吧,对于初学者对this这个关键字有好多人都不是很理解. 这里的this是指当前对象自己.

  我写一段代码: public classs ChenShun{

  public String name;

  public String setName (String name){

  this.name=name;

  System.out.println(this);//这里我打印this

}(注意:这段代码我中的name变量我用颜色区分一下表示,相同的颜色表示他们代表着同一个变量)

  public class A{

    ........

  ChenShun chenshun=new ChenShun();

  chenshun.setName("威猛叔叔");//这里我掉用了 ChenShun类中的setName方法

  System.out.println(chenshun);//这里我打印chenshun这个对象

    .......

}

结果是两处打印的值是相同的,从这里我们可以得出一个信息这里的this指代的就是chenshun这个对象,好了this就先讲到这里

我们接着看一下这个源代码 return(this==obj);我想对于 "==""这个符号大家都是不太陌生的吧,什么!你没见过这个符号! 0_0

好吧我就稍微的说一下"=="这个符号吧

附录:"==" 表示比较的是2个对象的地址 我想大家应该是知道的 数据的类型是分为两个大类的,值类型,和引用类型,如果这不清楚,我觉得你的基础简直不要不要的

但是为了大家我还是要说明一下的,看代码

借用上面定义的类

ChenShun chenshun =new ChenShun();

别小看这一句话,有很多面试的考官会问你,这句话做了什么,如果你不理解,那就呵呵了!

首先我这里要说两个东西,堆(Heap)和栈(Stack),你可以先理解为他们是分别的两个存储空间.

好了我们接着看代码

ChenShun chenshun; //这叫做声明,我声明了一个叫chenshun的Chenshun类型变量.

new ChenShun();//这叫做创建,我创建了一个ChenShun的对象.

chenshun=new ChenShun();//将对象"赋值"给chenshun这个变量,其实这样说,我个人认为说的也不是很准确.主要在于"赋值"这个"值"字上,这个值是什么呢?

再回头看一下这个代码,ChenShun chenshun=new ChenShun();

我用着样一句话说明一下,创建一个"引用" (ChenShun chenshun),创建一个"对象"(new ChenShun();),JAVA模型中有堆和栈两个内存空间,将引用放入栈中,将对象放入堆中"引用的值就是对象在堆中的地址","对象的值就是实际数据",这就是引用类型.

Java的8种特殊类型数据: byte,short,int,long,char,boolean,float,double .这几种类型不是对象其值直接放入栈中  看代码 int i=1 ; i放入栈中 值是1,而值不是对象的地址,这就是值类型.

 

 

好了这里就可以回归到"==""符号比较的是两个对象的地址.

那么这样看代码:

ChenShun chenshun=new ChenShun();

ChenShun chenshun1=new ChenShun();

ChenShen chenshun2=chenshun;

System.out.println(chenshun==chenshun1);//false

System.out.println(chenshun2==chenshun);//true

这样,我想你们就应该知道 他比较的是什么东西了吧!

如果仔细的看我写的东西,当然限于初学者,看到了8种基本类型中没有String类型,我想你们应该可以猜到了String是不属于基础类型的,我之前有说过,

JAVA的数据类型只有两种,分别是值类型,和引用类型,没错String是属于引用类型的,这里有点扯远了,但是我觉得还是有必要将String说一下,因为

有很多的面试官喜欢问这个问题.

String 是字符串,这我想大家都是知道的,那什么是字符串呢,要是让你写一个字符串,你可以马上抬手打出来.

看代码:

String x="abc";

是吧很简单的,但是你深入的了解过吗?我想许多的初学者都没有了解过,但是面试官问起来,你就是不知怎么去回答.

好吧,你可以这样去理解字符串,所闻的字符串,就是许多的字符连接起来,你肯点想,我靠这是什么回答,我只想说,这就是字符串的定义

让我们看看,字符串是许多的字符连接起来就是字符串,这就是答案.

不服我们看看源代码:

Java.lang.String

public final class String implements java.io.Serializable, Comparable<String>, CharSequence{ //注意这行代码我标红的位置,我想你们应该知道这个关键字的用途.

.........  

  public String(){

    this.value = new char[0]; //看到没,new char[0],创建了一个空的字符数组,你不服我不辩,这里就证明了我上面说的,所谓的字符串就是许多的字符串在一起

  }

........

}

关于更多的String这方面的东西我就不再这了深入的表述了,让我们回到正题上,我有时间的话会写一篇关于String的详解出来

有的人会问,你说比较方面的东西怎么会扯到String上面去,我只想这样回答,String是特殊的比较对象,我们要特殊的回答.

看代码:

String x="abc";

String y="abc";

String k=new String("abc");

String h=new String("abc");

String m=k;

System.out.println(x==y);//这个结果是什么?

System.out.println(x==k);//这个结果是什么?

System.out.println(k==h);//这个结果是什么?

System.out.println(m==k);//这个结果是什么?

仔细想想

答案是:true,false,false,,true

有的人,心想,我靠这TMD不科学 为什么 x==y的值是true,你不是说,String是引用类型吗? 那String x="abc"; 和 String y="abc" 不是创建了两个对象吗?

我想这样告诉你们,哈哈你们太天真了,这里有一个面试官常会问的问题,String 是不是可变的类型,答:"不是!",String是不可变类型的

String类型的本质是char[] 

还记得上面String类源代码我写的什么吗?

public String(){

  this.value=new char[0];

}

对了这里的value属性,在String类中, value属性是这样定义的:private final char value[]; ╮(╯▽╰)╭不要怪我套路深,这是没有办法的,不这样坑你们一下,你们很难记住.

那好,问题来了,既然是不可变的话,我们有时候又喜欢用到字符串就随意定义一个,这样会对内存造成很大的消耗,那怎么办?有时候就重复的定义了相同的字符串!

好吧Java帮我们想出了一个办法,只要你的程序在运行的时候,我就给你开辟一个区域(StringPool)也叫作String池.

String池用来存放运行时产生的各种字符串,并且池中的字符串不存在重复.OK这就是原因了,有重复的字符串他就直接将引用了池中的字符串了,所以

System.out.println(x==y); 结果为true 因为当创建 String x="abc";的时候就将 字符串放入到了 String池中,当你 String y="abc";的时候,Java会到

String池中找是否有字符串"abc",如果存在"abc"就将该字符串的内存地址直接给y,好了这就是为什么 x==y.

那有的人又要问了,那为什么x==k的结果是false,那是因为 new 这个关键字造成的,你们可以这样记住,在Java中只要使用new关键字创建对象,那么一定会在(堆区或栈区)创建一个新的对象,换句话,new关键字创建的对象在堆或是栈中,String x="abc";这样写的代码生成的是在String池中,位置都不一样,这样肯定结果就是false.

这样大致上的"=="比较符算是讲的差不多了,有些知识需要自己去百度,百度才是你最好的老师!

又回到之前所讲的东西了equals(),(终于讲回来了),为了避免你往上翻,那么我就在打一遍 object类中的equals()的源码

看代码:

public boolean equals(Object obj){

  return (this==obj);

}

咦?你肯定会奇怪了 这里用的是== 符号,怎么和我实际用到有点不一样啊,既然是==符号,那我还用什么equals(),直接==就可以了.

如果你这么想,好吧,我之呵呵不说话

看代码:

  String str= new String("abc");

  String str1=new String("abc");

  System.out.println(str.equals(str1));//结果是什么呢? 仔细想想

答案是: true

WTF,这和之前的分析是不同的.

这里又有一个要讲,那就是String重写了equals()方法.

看代码:

Java.lang.String

  public final class String  implements java.io.Serializable, Comparable<String>, CharSequence{

  ........

  public booleam equals( Object anobject){

    if(this==anobject){

      return true;

    }

    if(anobject instanceof String){ //这里的 instanceof 关键字用于判断一个引用类型变量所指向的对象是否是一个类(或接口、抽象类、父类)的实例

      String anotherString=(String)anobject;

      int n=value.length;

      if(n==anotherString.value.length){      

        char v1[] = value;
        char v2[] = anotherString.value;
        int i = 0;
        while (n-- != 0) {
          if (v1[i] != v2[i])
          return false;
          i++;
        }
        return true;

      }

    }

     return false;

  }

  ........

  }

  

可以看出这是重写了equals(),这里就是比较了两个 字符串是否是一样的,好了,但是有一点要记住只要重写了 equals() 有必要重写hashCode();

接下来我们来看一下

Object类中的 hashCode()方法,我会在后面说名 hashCode()

看代码:

Java.lang.Object

  public native int hashCode();//在Object类中就是这样的一行代码,注意我这里标红的 关键字native,好吧对于初学者,这个关键字实在是用的太少了,基本上是不用这个关键字,但是我还是在这里稍微的说明一下这个关键字的用途.

要知道Java是不完美的语言,Java是无法直接访问操作系统的底层的,怎么办,对了C语言可以啊,那好吧,那我就叫C语言帮我在底层做一些操作,但是我怎么让C语言和我Java进行沟通呢,这时候我们就用到了native关键字,这就像是Java和C之间的一个接口,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的,这其实就涉及到了Java的底层,初学者现在不必过余的去深究,这里你就看成我在这里调用了一个C写好的hashCode()方法.

好的,让我来了解一下hashCode();

我们来先看看权威的说明(官方的说明)

  1. hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。   
  2. hashCode 的常规协定是:   
  3. 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。   
  4. 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。   
  5. 以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。   
  6. 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)  
  7. 当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

对于初学者来说有点太官方了是吧,那就看看下面的总结(别人写的,我Ctrl+C过来的,这是我之前看的微博,觉得总结的很漂亮)

1.hashCode主要是用来快速的查找,你可以理解为是一个索引,比如查找HashTable,HashMap,HashCode主要是用来在散列存储结构中确定对象的存储地址的.

2.如果两个对象相同,就是适用于equals(Java.lang.Object) 方法,那么这两个对象的hashCode一定要相同

3.如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点

4.两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”

好了这就是总结,我无耻的笑了一下.

再归纳一下就是hashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的。以下这段话是从别人帖子回复拷贝过来的:

1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有  
例如内存中有这样的位置  
0  1  2  3  4  5  6  7    
而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。  
但如果用hashcode那就会使效率提高很多。  
我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,然后把我们的类存放在取得得余数那个位置。比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。这样,以后在查找该类时就可以通过ID除 8求余数直接找到存放的位置了。  
  
2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和17除以8的余数都是1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要定义 equals了。  
也就是说,我们先通过 hashcode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。  
那么。重写了equals(),为什么还要重写hashCode()呢?  
想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊  

在这里我贴出Java.lang.String类的hashCode源码

  public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

  

最后我总结一下吧

1.对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;

    如果作用于引用类型的变量,则比较的是所指向的对象的地址

2.对于equals方法,注意:equals方法不能作用于基本数据类型的变量

    如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;

    诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

3.hashCode是用来查找用的,如果重写了equals,那么请把hashCode也重写一下,不然放入hashSet中会出现问题的,具体什么问题希望你们自己尝试一下.

 

好了,博文就写到这里了,妈呀,累死我了,如果我有什么没写对的请一定要指出来,感激不尽~~~~~~?(^?^*)

 

以上是关于Java ==,equals,hashCode,解析(适合初学者阅读)的主要内容,如果未能解决你的问题,请参考以下文章

Java中关于equals()和hashCode()的问题

在java中,关于equals(),和hashCode()的重写问题。

java hashCode, 引用以及equals().

java学习-- equals和hashCode的关系

Java:Effective java学习笔记之 覆盖equals时总要覆盖hashcode

java 集合中重写hashCode方法和重写equals方法啥关系?