文章目录
目录
三、String.equals()源代码深度解析(基于 JDK 8)
前言
在 Java 中,String
类的 equals()
方法是用于比较两个字符串的内容是否相等的重要方法,该方法接收一个 Object
类型的参数 anObject
,返回一个布尔值,表示当前 String
对象与传入对象的内容是否相等。在 Java 中,String
类的equals()
方法是重写自Object
类的核心方法,其设计目标是精确判断两个字符串的字符序列是否完全一致。相较于Object
类默认的 “引用地址比较”,String.equals()
实现了 “内容比较”,这一特性使其成为日常开发中最常用的方法之一。下面我们从设计逻辑、源代码细节、特殊场景等方面进行深度解读。
一、equals()方法是怎么来的?
- 首先我们肯定知道equals()方法源自于Object类,我们知道Java 中的所有类都直接或间接继承自
Object
类,equals()
方法就是Object
类中的一个方法。Object
类是 Java 类层次结构的根类,它为所有类提供了一些基本的通用方法,equals()
就是其中之一,用于对象之间的比较。在Object
类中,equals()
方法的默认实现是比较两个对象的内存地址,即判断两个引用是否指向同一个对象。
二、equals()
的核心作用与设计背景
-
核心作用
用于比较两个字符串的字符内容是否完全相同,包括字符序列、长度、大小写(大小写敏感)等。例如:"hello".equals("hello")
→true
(内容完全一致)"hello".equals("Hello")
→false
(大小写不同)"hello".equals("hell")
→false
(长度不同)
-
与
Object.equals()
的区别 Object
类的equals()
默认比较对象引用地址(等价于==
),源码为:public boolean equals(Object obj) { return (this == obj); // 仅判断是否为同一个对象 }
String
类重写后,改为比较字符内容,更符合字符串 “值相等” 的业务需求。
三、String.equals()
源代码深度解析(基于 JDK 8)
String
类的equals()
方法源代码如下,其逻辑经过多层优化,兼顾正确性与效率:
public boolean equals(Object anObject) {
// 步骤1:判断是否为同一个对象(引用地址相同)
if (this == anObject) {
return true;
}
// 步骤2:判断参数是否为String类型(非String直接返回false)
if (anObject instanceof String) {
String anotherString = (String)anObject; // 强制类型转换
int n = value.length; // 当前字符串的长度(value是存储字符的底层数组)
// 步骤3:比较字符串长度(长度不同则内容一定不同)
if (n == anotherString.value.length) {
char v1[] = value; // 当前字符串的字符数组
char v2[] = anotherString.value; // 目标字符串的字符数组
int i = 0;
// 步骤4:逐个比较字符(直到所有字符相同或发现差异)
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
// 非String类型或其他情况,返回false
return false;
}
关键步骤的设计逻辑与优化点
-
步骤 1:先比较引用地址(
this == anObject
)- 若两个字符串引用指向同一个对象(内存地址相同),则内容必然相同,直接返回
true
。 - 这是最高效的短路判断,避免后续所有操作(尤其长字符串的字符比较),时间复杂度为
O(1)
- 若两个字符串引用指向同一个对象(内存地址相同),则内容必然相同,直接返回
String s1 = "abc";
String s2 = s1;
s1.equals(s2); // 触发步骤1,直接返回true
步骤 2:判断参数类型(anObject instanceof String
)
-
- 仅当参数是
String
类型时才继续比较,否则直接返回false
。 - 为什么用
instanceof
?instanceof
会自动处理null
:若anObject
为null
,instanceof
返回false
,避免NullPointerException
(无需单独写if (anObject == null)
判断)。- 确保类型匹配:例如
"abc".equals(new StringBuilder("abc"))
会返回false
(因StringBuilder
不是String
类型)。
- 仅当参数是
-
步骤 3:比较字符串长度(
n == anotherString.value.length
) -
String
底层用private final char value[]
数组存储字符,value.length
即字符串长度。- 若两个字符串长度不同,内容一定不同,直接返回
false
,避免无效的字符遍历(优化点:减少O(n)
操作)。
-
示例:
"abcd".equals("abc")
→ 长度 4≠3,步骤 3 直接返回false
。 -
步骤 4:逐个比较字符(
v1[i] != v2[i]
)- 若长度相同,遍历两个字符数组,逐个比较对应索引的字符。
- 一旦发现不同字符(如
v1[2] != v2[2]
),立即返回false
(提前退出,减少不必要的比较)。 - 若所有字符都相同(循环结束),返回
true
。 - 字符比较基于Unicode 编码值(例如
'a'
的编码是 97,'A'
是 65,因此大小写不同会返回false
)。 - 循环中
n-- != 0
的写法:先判断n
是否为 0,再自减,等价于从索引0
遍历到n-1
。
四、特殊场景与注意事项
-
与
null
比较String.equals(null)
返回false
(因null instanceof String
为false
),不会抛异常。- 反例:若参数在前(如
null.equals("abc")
),会直接抛NullPointerException
,因此推荐写法是常量在前:
-
空字符串
空字符串的""
的比较value
数组长度为 0,因此:"" equals(" ")
→false
(后者长度为 1,且字符为空格)。"" equals("")
→true
(长度相同,且无字符差异)。
-
与其他类型对象的比较
若参数不是String
类型(如StringBuffer
、Object
等),即使内容相同,equals()
也返回false
:-
String s = "abc"; StringBuffer sb = new StringBuffer("abc"); s.equals(sb); // false(sb不是String类型)
-
-
大小写敏感问题
equals()
严格区分大小写,若需忽略大小写,应使用equalsIgnoreCase()
:-
"Hello".equals("hello"); // false "Hello".equalsIgnoreCase("hello"); // true
-
-
字符串常量池的影响
对于字符串常量(如"abc"
),JVM 会将其存入常量池,相同内容的常量会指向同一对象,此时==
也会返回true
:-
String s1 = "abc"; String s2 = "abc"; s1 == s2; // true(常量池同一对象) s1.equals(s2); // true(内容相同)
- 但通过
new String("abc")
创建的对象会在堆中生成新实例,此时==
返回false
,但equals()
仍返回true
。
-
五、equals()
的性能特性
- 时间复杂度:
- 最好情况:
O(1)
(步骤 1、2、3 直接返回,无需比较字符)。 - 最坏情况:
O(n)
(需比较所有n
个字符,如两个完全相同的长字符串)。
- 最好情况:
- 优化逻辑:
方法通过 “先判断引用→再判断类型→再判断长度→最后比较字符” 的顺序,尽可能早地排除不相等的情况,减少无效计算,体现了高效的设计思路。
总结
String.equals()
是 Java 中 “内容相等” 判断的标准实现,其核心逻辑是通过多层优化的比较步骤,精确判断两个字符串的字符序列是否一致。理解其源代码不仅能掌握字符串比较的细节,还能学习到 “短路判断”“类型校验” 等代码优化思想,对重写其他类的equals()
方法也有借鉴意义。
好了,今天依旧是深蹲不写BUG,我们一起努力!!!