Java基础系列之你真的懂==与equals的区别吗?
对于Java初学者而言,可能会对这两个比较方法比较模糊,有的人可能会觉得两个的方法使用起来结果是一样的等。如果你有这样的想法,我建议你来看看这边博客,让你充分了解这两个比较的异同,以及他们底层是如何比较的等。阅读这篇文章之前,我希望你是对Integer和String这两个类是有所了解的,否则可以参考一下博客以加深你对这两个类的理解:
那么,下面我将使用一些例子来引入本篇博客的主题,看看你能做对多少个题吧!
public static void main(String[] args) {
int i1 = 8;
int i2 = 8;
Integer i3 = 8;
Integer i4 = new Integer(8);
Integer i41 = new Integer(8);
Integer i5 = 129;
Integer i6 = 129;
int i7 = 129;
System.out.print(i1 == i2);
System.out.print(" ");
System.out.print(i1 == i3);
System.out.print(" ");
System.out.print(i1 == i4);
System.out.print(" ");
System.out.print(i4 == i41);
System.out.print(" ");
System.out.print(i5 == i6);
System.out.print(" ");
System.out.print(i5 == i7);
System.out.print(" ");
System.out.print(i5.equals(i7));
}
上面运行的结果为 true true true false false true true,第一个i1 == i2 结果为true ,这个可以理解,因为两个都是int基本数据类型,并且值相等;第二个比较 i1 == i3 结果也为true,i3为int基本数据类型对应的引用类型,那么它们为什么相等呢?这个我们需要怎么去验证呢?其实可以通过字节码看编译器是如何执行这些代码就可以了;通过字节码发现,在执行 == 的时候i3变量是先执行intValue()这方法,这个方法的作用就是返回Integer类型对应的int值。所以 i1 和i3的比较最终还是变成了 i1 == i2的比较。
字节码文件:
intValue方法的源码:
/**
* Returns the value of this {@code Integer} as an
* {@code int}.
*/
public int intValue() {
return value;
}
直接返回了 value这个变量,而这个变量就是int类型的全局变量:
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
我们再回到上面i1 == i4 的问题,i1是基本类型,i4是引用类型,其实还是回到了第二种情况,所以他们还是true。
i4 == i41 两个都是引用类型,并且都是用了new的关键字,我们知道只要调用new关键字那么他们的引用地址肯定不一样,他们的结果返回了false,说明引用类型使用==比较时比较的是他们的内存地址,而不是字面值。
i5 == i6 : 两个都是引用类型,并且值相等,但是结果是false,通过看字节码可以知道,编译器在编译的是时候,这种直接赋值的操作实际上调用了Integer中的valueOf方法,不清楚这个方法的同学建议看一下上面关于Integer的博客。Integer这个类为了提高速度,缓存了-127 至 128 的数,当调用valueOf的时候,如果传进来的数在这个范围之内,那么直接返回缓存的数据,否则new一个对象出来,然后返回。这个例子中,两个值都超出返回,所以都new了一个对象。这个是在开发过程中经常遇到的坑,解决办法是使用equals方法,这个方法已经被重写了,其过程是将引用类型转成基本类型,然后使用 == 来比较大小;
i5 == i7: 一个基本数据类型,一个引用类型,且超出缓存范围,结果为true。这个不难理解,引用类型在编译的时候会自动拆箱,所以最后比较的是基本类型,所以为true。
i5.equals(i7): 上面也有提到Integer已经重写了equals方法,所以跟上一种情况是一致的,为true。
在String类中也会有比较多的面试会遇到,比如说下面代码示例:
public static void main(String[] args) {
String str1 = "aa";
String str2 = "aa";
String str3 = new String("aa");
System.out.println(str1 == str2);//true
System.out.println(str1 == str3);//false
System.out.println(str1.equals(str3));//true
}
看到这里,可能会有同学会问,String每创建一次就会生成一个新对象,所以他们的内存地址肯定不一样,但是str1 == str2 返回true,这说明明 == 比较的是字面量吗?答案是否定的,String在我们日常开发中使用的频率最高的类,频繁的创建对象势必对应用的性能有一定的影响,java官方为了提高性能,每次使用字面量声明字符串时都会先向缓存池中查找池中是否已存在该对象,如果有则直接引用该对象,如果没有则new一个对象出来,然后将该对象放入缓存池中。上面str1 和str2因为字面量是相同的,所以他们共用缓存池中的一个对象,所以他们指向的内存地址是一样的,故为true;
str3是直接new出来的,所以他是不经过缓存池的,因此str1 和 str3是不同的。
String 的equals方法也已经重写了object的equals方法,它首先比较两个字符串的内存地址,不一样了才对两个字符串字面量进行比较。
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;
}
划重点:== 与 equals 的区别
对于未重写Object类中的equals方法,两者的作用是一样的,因为Object的equals方法也是使用 == 来判断两个对象是否相等的。
== 操作符对于基本类型数据则比较的是他们的字面量的值是否相等,只要有一个是基本类型,那么另一个如果的封装类型,则会自动拆箱;对于两个类型都是引用类型,则比较的是两个对象的内存地址的值是否相等。
equals方法如果未被重写,与 == 无异。一般地,我们都会重写Object中equals方法,比如上面的Integer和String都重写的equals方法,该方法比较的是字面量的值是否相等,有别于 ==。
欢迎大家关注公众号: 【java解忧杂货铺】,里面会不定时发布一些技术博客;关注即可免费领取大量最新,最流行的技术教学视频:



Java基础系列之你真的懂==与equals的区别吗?的更多相关文章
- 夯实Java基础系列6:一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别!
目录 抽象类介绍 为什么要用抽象类 一个抽象类小故事 一个抽象类小游戏 接口介绍 接口与类相似点: 接口与类的区别: 接口特性 抽象类和接口的区别 接口的使用: 接口最佳实践:设计模式中的工厂模式 接 ...
- 夯实Java基础系列3:一文搞懂String常见面试题,从基础到实战,更有原理分析和源码解析!
目录 目录 string基础 Java String 类 创建字符串 StringDemo.java 文件代码: String基本用法 创建String对象的常用方法 String中常用的方法,用法如 ...
- 夯实Java基础系列7:一文读懂Java 代码块和执行顺序
目录 Java中的构造方法 构造方法简介 构造方法实例 例 1 例 2 Java中的几种构造方法详解 普通构造方法 默认构造方法 重载构造方法 java子类构造方法调用父类构造方法 Java中的代码块 ...
- 夯实Java基础系列10:深入理解Java中的异常体系
目录 为什么要使用异常 异常基本定义 异常体系 初识异常 异常和错误 异常的处理方式 "不负责任"的throws 纠结的finally throw : JRE也使用的关键字 异常调 ...
- 夯实Java基础系列15:Java注解简介和最佳实践
Java注解简介 注解如同标签 Java 注解概述 什么是注解? 注解的用处 注解的原理 元注解 JDK里的注解 注解处理器实战 不同类型的注解 类注解 方法注解 参数注解 变量注解 Java注解相关 ...
- Java基础系列--HashMap(JDK1.8)
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10022092.html Java基础系列-HashMap 1.8 概述 HashMap是 ...
- 夯实Java基础系列1:Java面向对象三大特性(基础篇)
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 [https://github.com/h2pl/Java-Tutorial](https: ...
- 夯实Java基础系列4:一文了解final关键字的特性、使用方法,以及实现原理
目录 final使用 final变量 final修饰基本数据类型变量和引用 final类 final关键字的知识点 final关键字的最佳实践 final的用法 关于空白final final内存分配 ...
- 夯实Java基础系列5:Java文件和Java包结构
目录 Java中的包概念 包的作用 package 的目录结构 设置 CLASSPATH 系统变量 常用jar包 java软件包的类型 dt.jar rt.jar *.java文件的奥秘 *.Java ...
随机推荐
- JVM笔记9-Class类文件结构
1.Class类文件结构 Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个 Class 文件中 ...
- Airbnb/Apache Superset – the open source dashboards and visualization tool – first impressions and link to a demo
https://assemblinganalytics.com/post/airbnbapache-superset-first-impressions-and-link-to-a-demo/ Tod ...
- RDC去省赛玩前の日常训练 Chapter 1
4/3 技能点 A. 生成树的计数 论文:周冬<生成树的计数及其应用>(看不懂 pending) 一个栗子:Codeforces 719D 两个做法 Matrix-Tree + 高斯消元, ...
- 局部内部类访问它所在方法的局部变量时,要求该局部变量必须声明为final的原因
这是java的一条规则.那么为什么会有这条规则呢?要想弄懂这个问题,就需要弄懂局部内部类对象和局部变量的生命周期的谁更长的问题. 首先,看一段代码,以没有将变量声明为final的代码作为例子,代码如下 ...
- 银行卡号、电话号、身份证号 EditText 自定义格式的输入框
package com.yidian.AddSpaceEditText;import android.text.Editable;import android.text.InputFilter;imp ...
- Scrapy爬取豆瓣电影top250的电影数据、海报,MySQL存储
从GitHub得到完整项目(https://github.com/daleyzou/douban.git) 1.成果展示 数据库 本地海报图片 2.环境 (1)已安装Scrapy的Pycharm (2 ...
- Oracle数据库逻辑迁移之数据泵的注意事项
环境:数据迁移,版本 11.2.0.4 -> 12.2.0.1 思考: 对于DBA而言,常用物理方式的迁移,物理迁移的优势不必多说,使用这种方式不必担心对象前后不一致的情况,而这往往也解决了不懂 ...
- 洛谷 P1613 解题报告
P1613 跑路 题目描述 小\(A\)的工作不仅繁琐,更有苛刻的规定,要求小\(A\)每天早上在\(6:00\)之前到达公司,否则这个月工资清零.可是小\(A\)偏偏又有赖床的坏毛病.于是为了保住自 ...
- Nordic nRF51/nRF52开发环境搭建
本文将详述Nordic nRF51系列(包括nRF51822/nRF51802/nRF51422等)和nRF52系列(包括nRF52832/nRF52810/nRF52840)开发环境搭建. 1. 强 ...
- BootStrap 常用控件总结
下拉选择Select2:http://ivaynberg.github.io/select2/index.html 文件上传bootstrap-fileinput:https://github.com ...