相信很多读者关于==equals懂了又懵,懵了又懂,如此循环,事实上可能是因为看到的博客文章之类的太多了,长篇大论,加上一段时间的洗礼之后就迷路了。本篇文章再一次理清楚。当然如果觉得本文太啰嗦的话,当然我也考虑到了,因为我也不喜欢长篇大论啰里啰嗦比比叨叨胡搅蛮缠的文章,毕竟大家入门java 的时候就知道个大概了,因此记住一句话就好了:equals本身和 == 没有区别,对于基本数据都是比较值,对于引用类型,则比较的是所指向的对象的地址!其他类在继承Object类之后对equals方法重写,所以表现的是比较里面的内容!具体比较的则要看自己是怎么重写的。

好了,如果有兴趣的就看下文,当然不感兴趣的大佬可以点个赞直接走了,不用看了,会了还看个*啊,楼主你个憨憨(皮一下很开心)

1、原生的equals()方法本身与 “ == ”没有任何区别!

从java语言本质上来讲,"=="属于JAVA语言的运算符,而equals则是根类Object的一个方法。

关于Object类的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()方法本身与 “ == ”没有任何区别!唯一的区别则是基本类型没有继承Object类,所以基本类型没有equals()方法,也就是说基本类型只能使用“ == ”判断值是否相等。

既然原生的equals()方法本身与 “ == ”没有任何区别,那么我们对运算符 “ == ”的使用有所了解即可!

运算符 “ == ”其具体作用是用来比较值是否相等,这里分两中情况:

  • 1、基本数据类型的变量,则直接比较其存储的 “值”是否相等;

  • 2、引用类型的变量,则比较的是所指向的对象的地址是否相等;

到这里我们可以初步确认原生的equals()方法本身与 “ == ”没有任何区别!作用正是如上。

2、equals()方法的重写

但是重点来了,因为对于equals()方法我一直在强调原生二字。是的,让很多初学者疑惑的点就在这里:equals()方法的重写!

在JDK中,诸如StringDate等类对equals方法进行了重写,以String为例,这里感兴趣的读者可以一起看看String类中重写的equals()方法,当然跳过也问题不大

/**
* Compares this string to the specified object. The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object that represents the same sequence of characters as this
* object.
*
* @param anObject
* The object to compare this {@code String} against
*
* @return {@code true} if the given object represents a {@code String}
* equivalent to this string, {@code false} otherwise
*
* @see #compareTo(String)
* @see #equalsIgnoreCase(String)
*/
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
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;
}

从源码中可以看出,首先该方法判断比较的是所指向的对象地址是否相等,如果相同直接返回true,如果不相同进行下一个if判断,第二个if判断大致意思则是比较其存储的 “值”是否相等,也就是比较内容值!相同就返回true,比如两个new String对象“AAA”“AAA”,这里虽然对象地址不相等,但是内容相等,所以同样返回true。

这里我就想给各位出个典型的String例子了:

public static void main(String[] args) {
String a = "宜春";
String b = new String("宜春");
String c = b; //注意这里是引用传递,意思是c也指向b指向的内存地址 System.out.println(a == b); //false
System.out.println(a == c); //false
System.out.println(b == c); //true
System.out.println(a.equals(b)); //true
System.out.println(a.equals(c)); //true
System.out.println(b.equals(c)); //true
}

【特别注意:String类型属于引用类型】

解析:

(1)a == b?意思是地址指向的是同一块地方吗?很明显不一样。

(2)a == c?意思是地址指向的是同一块地方吗?很明显不一样。

(3)b == c?意思是地址指向的是同一块地方吗?很明显内容一样,所以为true。

(4)a.equals( b )?意思是地址指向的内容一样嘛?一样。

(4)a.equals( c )?意思是地址指向的内容一样嘛?一样。

(4)b.equals( c )?意思是地址指向的内容一样嘛?一样。

当然,你可能还是有点疑惑,那么结合下面这张图再理解上面的解析,你可能就恍然大悟了

OK。现在能理解嘛?你你你......不用回答,我知道你理解了(理直气壮)。当然值得注意一点的是String中intern()方法,先看一段程序:

    public static void main(String[] args) {
String a = "宜春";
String b = new String("宜春");
b=b.intern(); System.out.println(a == b); //true
System.out.println(a.equals(b)); //true
}

intern方法的意思是检查字符串池里是否存在,如果存在了那就直接返回为true。因此在这里首先a会在字符串池里面有一个,然后 b.intern()一看池子里有了,就不再新建new了,直接把b指向它。

3、为什么要重写equals方法?

不知道大家有没有想过这个问题。当然答案也是很简单的,因为程序员比较字符串一般比较其内容就好了,比较内存地址是不是同一个对象就好像没啥意义了,重写equals方法就很方便用来比较字符串的内容了。

其实除了诸如String、Date等类对equals方法进行重写,我们在实际开发中,我们也常常会根据自己的业务需求重写equals方法.

举个栗子:

我们的需求就是如果两个学生对象姓名、身份证号、性别相等,我们认为两个学生对象相等,不一定需要学生对象地址相同。

学生A的个人信息(姓名:如花,性别:女,身份证号:123,住址:广州),学生A对象地址为0x11,

学生B的个人信息(姓名:如花,性别:女,身份证号:123,住址:深圳),学生A对象地址为0x12,

这时候如果不重写Object的equals方法,那么返回的一定是false不相等,这个时候就需要我们根据自己的需求重写equals()方法了。具体equals方法的重写代码如下:

// 重写equals方法
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Student)) {
// instanceof 已经处理了obj = null的情况
return false;
}
Student stuObj = (Student) obj;
// 对象地址相等
if (this == stuObj) {
return true;
}
// 如果两个对象姓名、身份证号码、性别相等,我们认为两个对象相等
if (stuObj.name.equals(this.name) && stuObj.sex.equals(this.sex) && stuObj.IDnumber.equals(this.IDnumber)) {
return true;
} else {
return false;
}
}

开发中这样设计,才能符合我们的生活!到这里我就不信了你还搞不定==和equals!

可是一涉及重写equals方法的同时又衍生了下面一个问题。

4、重写equals方法之后要不要重写hashCode()方法?

当然这个问题要讨论,又要长篇大论哔哔一大堆了,有空写一篇这样的文章吧专门讨论讨论这个问题,当然园子里的大佬们也写了一大堆!可以自行去了解了解。本篇文章简单聊聊,点到即可。

首先hashCode()方法是Object类的一个方法,源码如下:

/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java<font size="-2"><sup>TM</sup></font> programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
public native int hashCode();

可以看出hashCode()方法返回的就是一个int数值,从方法的名称上就可以看出,其目的是生成一个hash码。hash码的主要用途就是在对对象进行散列的时候作为key输入,据此很容易推断出,我们需要每个对象的hash码尽可能不同,这样才能保证散列的存取性能。

事实上,Object类提供的默认实现确实保证每个对象的hash码不同(在对象的内存地址基础上经过特定算法返回一个hash码)。Java采用了哈希表的原理。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。初学者可以这样理解,hashCode方法实际上返回的就是对象存储的物理地址(实际上不是)。

想要知道hashCode的作用,必须要先知道Java中的集合。

Java中的集合List的元素是有序的,元素可以重复;Set元素无序,但元素不可重复。这我们都清楚。但是你有没有想过这样一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?

每错这里就是用Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。

那怎么解决呢?我们可以在Java集合框架中得到验证。由于HashSet是基于HashMap来实现的,所以这里只看HashMapput方法即可。源码如下:

public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
//这里通过哈希值定位到对象的大概存储位置
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//if语句中,先比较hashcode,再调用equals()比较
//由于“&&”具有短路的功能,只要hashcode不同,也无需再调用equals方法
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
addEntry(hash, key, value, i);
return null;
}

正如源码中注释所述,“&&”具有短路的功能,只要hashcode不同,也无需再调用equals方法。是的,Java采用了哈希表的原理。 哈希表具有优越的查询性能,就像九九乘法表2*3=6你能很轻易知道,但是哈希表难免还会出现哈希冲突,只是概率极低。

如此设计,这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。

如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

因此并不是重写了equals方法就一定要重写hashCode方法,只有用到HashMap,HashSet等Java集合的时候重写了equals方法就一定要重写hashCode方法。用不到哈希表仅仅重写equals()方法也OK的。

Java官方建议 重写equals()就一定要重写hashCode()方法。毕竟实际开发场景中常常用到Java集合

5、eqauls方法和hashCode方法关系

Java对于eqauls方法和hashCode方法是这样规定的:

1、如果两个对象equals为true ,他们的hashcode一定相等。

2、如果两个对象equals为false,他们的hashcode有可能相等。

3、如果两个对象hashcode相等,equals不一定为true。

4、如果两个对象hashcode不相等,equals一定为false。

最后,若有不足或者不正之处,欢迎指正批评,感激不尽!

欢迎各位关注我的公众号,里面有一些java学习资料和一大波java电子书籍,比如说周志明老师的深入java虚拟机、java编程思想、核心技术卷、大话设计模式、java并发编程实战.....都是java的圣经,不说了快上Tomcat车,咋们走!最主要的是一起探讨技术,向往技术,追求技术,说好了来了就是盆友喔...

听说你还搞不定java中的==和equals?的更多相关文章

  1. 2)Java中的==和equals

    Java中的==和equals   1.如果比较对象是值变量:只用==   2.如果比较对象是引用型变量:      ==:比较两个引用是不是指向同一个对象实例.      equals:       ...

  2. java中的==和equals的区别

    关于JAVA中的==和equals函数的区别 今天在研读Thinking in java 时注意到==和equals的区别,于是就通过查看JDK_API才读懂了他们的区别,于是将心得分享一下,望批评指 ...

  3. java中的==、equals()、hashCode()源码分析(转载)

    在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. ==  java中的==是比较两个对象在JVM中的地址.比较好理解.看下面的代码: ...

  4. 深入分析Java中的 == 和equals

    关于Java中的 == 和equals的解释请看这位博主的文章 :http://www.cnblogs.com/dolphin0520/p/3592500.html 以下是我对这篇文章的一些扩展. 对 ...

  5. java中的==、equals()、hashCode()

    java中的==.equals().hashCode()源码分析 在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. ==  java中 ...

  6. 轻松搞懂Java中的自旋锁

    前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...

  7. 一文彻底搞懂Java中的环境变量

    一文搞懂Java环境变量 记得刚接触Java,第一件事就是配环境变量,作为一个初学者,只知道环境变量怎样配,在加上各种IDE使我们能方便的开发,而忽略了其本质的东西,只知其然不知其所以然,随着不断的深 ...

  8. 一文搞懂 Java 中的枚举,写得非常好!

    知识点 概念 enum的全称为 enumeration, 是 JDK 1.5 中引入的新特性. 在Java中,被 enum关键字修饰的类型就是枚举类型.形式如下: enum Color { RED, ...

  9. 一文搞懂--Java中重写equals方法为什么要重写hashcode方法?

    Java中重写equals方法为什么要重写hashcode方法? 直接看下面的例子: 首先我们只重写equals()方法 public class Test { public static void ...

随机推荐

  1. 基于springboot1.5.9整合shiro时出现静态文件找不到的问题

    开门见山吧,上午对shiro进行整合了下,因为之前使用ssm框架对shiro框架整合过,所以觉得使用springboot再次对shiro框架进行整合也是没啥问题,但最后整合完之后,使用thymelea ...

  2. iview中遇到table的坑(已经修改了table的数据,但是界面没有更新)

    https://blog.csdn.net/bigdargon/article/details/89381466 https://blog.csdn.net/qiuyan_f/article/deta ...

  3. 什么是YUM

    什么是Yum Yum(全称为 Yellow dog Updater, Modified)是一个在RedHat以及CentOS等Linux系统中的Shell前端软件包管理器.基于RPM包管理,能够从指定 ...

  4. 写一个scrapy中间件--ip代理池

    middleware文件 # -*- coding: utf-8 -*- # Define here the models for your spider middleware # See docum ...

  5. 问题描述:判断一个整数 n 是否为 2 的幂次方

    一.2的幂次方的基本定义 什么样的数为2的幂次方?例如2^0=1,2^1=2,2^2=4……,符合公式2^n(n>=0)的数称为2的幂次方. 如何判断一个数是否为2的幂次方呢?基本思路:把一个数 ...

  6. Spark入门(一)--用Spark-Shell初尝Spark滋味

    Spark-Shell的使用 执行scala命令的spark-shell 进入spark的sbin目录,打开键入 ./spark-shell 即可进入spark-shell的目录 spark-shel ...

  7. C++ 理解类 和 类中的public、protected、private

    我们要明确,不只是C++有类,很多语言也会用到类,因为现在很多都是面向对象编程... 在c++中,关于类的理解,个人理解是这样的,具有共同属性的一个集合被称为类, 比如说人这个集合,具有性别,年龄,出 ...

  8. DVWA(七):XSS(stored)存储型XSS攻击

    存储型XSS : 存储XSS,会把攻击者的数据存储在服务器端,攻击行为将伴随着攻击数据一直存在.提交JS攻击代码存储到数据库然后再输出. low: 观察源码: <?php if( isset( ...

  9. 通过js自动判断移动终端设备(ios\android等)

    当用户用移动设备扫描一个二维码是,将扫描后的链接链接到一个页面,该页面只包含判断移动终端设备的js,判断好后自动跳转到对应的链接 或下载对应的内容. html代码如下: <script> ...

  10. 还记得第一个看到的Flutter组件吗?

    注意:无特殊说明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 MaterialApp 在学习Flutter的过程中我们第 ...