Java面试系列 No.1 —— equals 与 == 的区别

本文最后更新于:4 个月前

小声叭叭

最近想着通过面试题来巩固下自己的基础知识,在不少面试题总结中,“equals 与 == 的区别”这个问题出现频率不是一般高,虽然在自己编程中,有时候也会区分使用二者,但是既然是深究,那就来扒一扒这两者的区别。(看结论直接到最后一节【总结】)

看本质

首先,两者的作用呢都是用于比较,但是在本质上和使用上,两者还是有很大的区别的:

  • 本质上:==是Java的一个运算符,而equals()是Object类的一个用于比较的方法,这是根本上的不同。

  • 使用上:虽然在日常开发中,==equals()都会经常出现在比较两者是否相等的情况下,但是两者在适用场景上,却有着很大的不同。

先来看 “==”

首先,“==”在用于比较时,会有几种不同的情况:

  • 两侧为引用类型(封装类型)
  • 两侧为基本类型
  • 一侧为引用类型一侧为基本类型

两侧为基本类型

==两侧为基本类型时,==的作用为比较两者的值是否相等,比如以下的例子:

1
2
3
4
5
6
7
int a = 1222;
int b = 1222;
System.out.println(a == b);

输出:

true

对于java基础类型int的两个变量a和b,值均为1222,因此输出为true。同样也适用于Java其他的全部基本类型。

Java基础类型包含:

  • 数字类型:整数byte、short、int、long,浮点数:double、float
  • 字符类型:char
  • 布尔类型:boolean

两侧为引用(封装)类型

==两侧为引用(封装)类型时,比较的是引用是否相同,即除了要比较值是否相同外,还要比较值所在的内存地址是否相同.

对于绝大多数引用类型,只要使用了new关键字来创建新的对象,则对象的引用地址也将是新的地址,此时使用==得到的总为false。

而当第一个对象使用new,而将第一个对象直接复制给第二个对象时,第一个对象和第二个对象指向为同一个,此时使用==等到的总为true.

除开以上两种普遍情况外,需要考虑不同类型在定义时的一些不同情况,这里重点考虑几种常用的封装类型:

  1. String 类型

对于String,需要引入一个字符串常量池的概念,具体的内容大家自行搜索了解 我后续也会补上相关的文章,先立flag,补上后再回来贴地址,在这里我们需要了解一些内容:

为了提高性能并减少内存的开销,JVM在实例化字符串常量时进行了一系列的优化操作:

  1. 在JVM层面为字符串提供字符串常量池,可以理解为是一个缓存区;
  2. 创建字符串常量时,JVM会检查字符串常量池中是否存在这个字符串;
  3. 若字符串常量池中存在该字符串,则直接返回引用实例;若不存在,先实例化该字符串,并且,将该字符串放入字符串常量池中,以便于下次使用时,直接取用,达到缓存快速使用的效果。

例如如下的代码:

1
2
3
4
5
6
7
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2);

输出:

true

这种情况就适用于上述的字符串常量池说法,首先在定义str1时,会先检测常量池中是否有字符串”Hello”,若没有,则创建一个新的对象”Hello”保存在内存上,然后定义str2时,检测常量池中已存在字符串”Hello”,则会将str2也指向这个字符串,此时就会出现,str1和str2的值和引用均一致,所以使用 == 比较会出现true的情况。

  1. 基本类型的对应封装类型

基本类型的对应封装类型表如下:

基本类型对应封装类型
charCharacter
booleanBoolean
floatFloat
byteByte
doubleDouble
intInteger
shortShort
longLong

对于这些基础类型的封装类型,在Java 语言规范中,找到了关于==的这样一句话:

Java规范中关于==的描述

大致意思就是:

  • ==两侧为Integer类型时,在-128~127范围内时值相等则返回true;
  • ==两侧为Character类型时,在\u0000~\u007f范围内时值相等则返回true;
  • ==两侧为Boolean类型时,值相等则返回true。

上述范围内的值是直接缓存在内存中的,在直接赋值的情况下,均指向这个已缓存的对象,因此会出现在上述范围内值相等时使用==会返回true的情况。

Integer:

1
2
3
4
5
6
7
8
9
10
11
12
Integer i1 = 122;
Integer i2 = 122;
System.out.println(i1 == i2);

Integer i3 = 1222;
Integer i4 = 1222;
System.out.println(i3 == i4);

输出:

true
false

Character:

1
2
3
4
5
6
7
8
9
10
11
12
Character c1 = '\u0002';
Character c2 = '\u0002';
System.out.println(c1 == c2);

Character c3 = '\u00ff';
Character c4 = '\u00ff';
System.out.println(c3 == c4);

输出:

true
false

Boolean:

1
2
3
4
5
6
7
8
Boolean b1 = true;
Boolean b2 = true;

System.out.println(b1 == b2);

输出:

true

再看equals()

1
2
3
4
5
6

```java
public boolean equals(Object obj) {
return (this == obj);
}

由此可看到,默认情况下,equals()==的作用是一致的,因为在equals()中的运算还是使用==,对于两个引用类型使用equals(),默认情况下还是对引用进行比较,此时只需要结合==部分的讲解进行考虑即可。

但是,对于绝大多数的引用类型都会对equals()进行重写,就例如在上节介绍的Integer,String等一些Java内置引用类型,亦或是我们自己定义的一些引用类型,通常情况下都会将equals()重写为进行值的比较。例如如下的是String类型的equals()定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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()也都有各自的定义,这里不再赘述,自行查看即可。

结论

对于==

  • 基本类型:比较的是值是否相同;
  • 引用类型:比较的是引用是否相同;

对于equals()
本质上与==效果一致,但大多数情况下会重写为值的比较,所以equals()通常都用来进行值的比较。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!