文章目录
目录
三、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,我们一起努力!!!

5263

被折叠的 条评论
为什么被折叠?



