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 ...
随机推荐
- SignUtil
最近接的新项目 加密比较多 我就记录下. SignUtil是jnewsdk-mer-1.0.0.jar com.jnewsdk.util中的一个工具类.由于我没有百度到对应的信息.所以我只能看源码 ...
- php添加日志文件
记录一下. 有时候写测试代码的时候,不习惯直接在屏幕上输出反馈,那么可以配置日志文件,把需要输出的内容追加到日志文件里面,就很方便. Php自带日志系统,可以参考网上的博客配置. 我要说的是,如果你的 ...
- Mysql研磨之InnoDB行锁模式
事务并发带来的一些问题 (1)更新丢失(LostUpdate):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题最后的更新覆盖了由其 ...
- LCA最近公共祖先(倍增版)
倍增版LCA lac即最近公共祖先,u和v最近公共祖先就是两节点公用的祖先中深度最大的 比如 其中 lca(1,2)=4, lca(2,3)=4, lca(3,5)=1, lca(2,5)=4; 如何 ...
- redis 设置
设置成服务命令,redis目录下,执行cmd命令 redis-server --service-install redis.windows-service.conf --loglevel verbos ...
- oracle数据库语句积累
1.从一个表选出数据更新另一个表(后面的exists一定要加) update jqhdzt set shid = (select shid from v_plat_userjqinfo t where ...
- ArcCore重构-Platform_Types.h实现辨析
AUTOSAR定义了一系列PlatformTypes,如uint8/uint16/uint32等等基本类型. It contains all platform dependent types and ...
- SSM博客登录注册
我的博客采用的是 spring+springmvc+mybatis框架,用maven和git管理项目,之后的其他功能还有待进一步的学习. 首先新建一个maven项目,我的项目组成大概就这样, 建立好项 ...
- Java(五、类和对象中的例题)
一.方法中的参数为数值型的(int) import java.util.Scanner; public class ScoreCalc { public void calc(int num1,int ...
- KVM内核文档阅读笔记
KVM在内核中有丰富的文档,位置在Documentation/virtual/kvm/. 00-INDEX:整个目录的索引及介绍文档. api.txt:KVM用户空间API,所谓的API主要是通过io ...