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