Java 实例 – 字符串比较
在 Java 中,字符串比较是高频操作,核心需区分 内容比较 和 引用比较,避免因误用
1.
2.
3.
4.
== 和 equals() 导致逻辑错误。本文通过实例详解 4 种常用的字符串比较方式、适用场景及注意事项。
一、核心概念:内容比较 vs 引用比较
- 内容比较:判断两个字符串的字符序列是否完全一致(如 "abc" 和 "abc" 内容相同)。
- 引用比较:判断两个字符串变量是否指向堆内存中的同一个对象(即地址是否相同)。
Java 中字符串有两种创建方式,直接影响引用结果:
// 1. 字符串常量池(复用已有对象,引用相同)
String s1 = "abc";
String s2 = "abc"; // s1 和 s2 指向常量池同一个对象
// 2. 堆内存新对象(每次创建新地址,引用不同)
String s3 = new String("abc");
String s4 = new String("abc"); // s3 和 s4 指向堆中不同对象
二、4 种常用比较方式(实例详解)
1. == 运算符:引用比较(地址是否相同)
== 是 Java 原生运算符,用于判断两个变量的内存地址是否一致,不关心字符串内容。实例代码:
public class StringCompareDemo {
public static void main(String[] args) {
// 常量池创建
String s1 = "abc";
String s2 = "abc";
// 堆内存创建
String s3 = new String("abc");
String s4 = new String("abc");
// 常量池 vs 堆对象
String s5 = "ab" + "c"; // 编译期优化为 "abc",指向常量池
String s6 = new String("ab") + new String("c"); // 堆中新建对象
// 打印比较结果
System.out.println("s1 == s2: " + (s1 == s2)); // true(同常量池地址)
System.out.println("s3 == s4: " + (s3 == s4)); // false(不同堆地址)
System.out.println("s1 == s3: " + (s1 == s3)); // false(常量池 vs 堆)
System.out.println("s1 == s5: " + (s1 == s5)); // true(编译期优化为同一常量)
System.out.println("s1 == s6: " + (s1 == s6)); // false(堆中拼接的新对象)
}
}
结论:
- 仅当两个字符串变量指向 同一个对象 时,
==才返回true。 - 切勿用
==比较字符串内容(除非明确确认是同一对象)。
2. equals() 方法:内容比较(默认区分大小写)
String 类重写了 Object 的 equals() 方法,专门用于 比较字符串内容是否完全一致(区分大小写、空格、字符顺序)。实例代码:
public class EqualsDemo {
public static void main(String[] args) {
String s1 = "abc123";
String s2 = new String("abc123");
String s3 = "ABC123"; // 大小写不同
String s4 = "abc 123"; // 多空格
// 内容完全一致(忽略创建方式)
System.out.println("s1.equals(s2): " + s1.equals(s2)); // true
// 内容不一致(大小写、空格差异)
System.out.println("s1.equals(s3): " + s1.equals(s3)); // false
System.out.println("s1.equals(s4): " + s1.equals(s4)); // false
// 空值安全:避免 NullPointerException(推荐写法)
String s5 = null;
System.out.println("s1.equals(s5): " + s1.equals(s5)); // false(s1 非空,直接比较)
// System.out.println(s5.equals(s1)); // 报错:NullPointerException
}
}
核心逻辑(String 源码简化):
public boolean equals(Object anObject) {
if (this == anObject) return true; // 先判断引用,相同直接返回 true
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) { // 长度不同直接返回 false
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()(如s1.equals(s5)而非s5.equals(s1))。
3. equalsIgnoreCase() 方法:忽略大小写的内容比较
专门用于 不区分大小写的内容比较(仅忽略大小写,其他细节如空格、字符顺序仍需一致)。
实例代码:
public class EqualsIgnoreCaseDemo {
public static void main(String[] args) {
String username1 = "Admin";
String username2 = "admin";
String username3 = "ADMIN123";
// 忽略大小写比较
System.out.println("username1.equalsIgnoreCase(username2): " + username1.equalsIgnoreCase(username2)); // true
System.out.println("username1.equalsIgnoreCase(username3): " + username1.equalsIgnoreCase(username3)); // false(多了123)
// 实际场景:用户登录用户名校验
String inputUsername = "admin";
String dbUsername = "Admin";
if (inputUsername.equalsIgnoreCase(dbUsername)) {
System.out.println("用户名校验通过");
} else {
System.out.println("用户名错误");
}
}
}
结论:
- 适用于用户名、验证码等 不区分大小写 的场景。
- 仍需注意其他字符细节(如空格、特殊字符)是否一致。
4. compareTo() 方法:按字典序比较(支持排序)
compareTo() 是 Comparable 接口的实现方法,按 Unicode 编码顺序 比较字符串,返回整数结果,支持字符串排序。返回值规则:
- 返回
0:内容完全一致(同equals())。 - 返回 正数:当前字符串在字典序中位于参数字符串之后(如 "b" 比 "a" 大,返回 1)。
- 返回 负数:当前字符串在字典序中位于参数字符串之前(如 "a" 比 "b" 小,返回 -1)。
实例代码:
public class CompareToDemo {
public static void main(String[] args) {
String a = "apple";
String b = "banana";
String c = "Apple";
String d = "app";
// 比较结果
System.out.println("a.compareTo(b): " + a.compareTo(b)); // -1(a 在 b 前)
System.out.println("b.compareTo(a): " + b.compareTo(a)); // 1(b 在 a 后)
System.out.println("a.compareTo(c): " + a.compareTo(c)); // 32('a' Unicode 97,'A' 65,97-65=32)
System.out.println("a.compareTo(d): " + a.compareTo(d)); // 2(a 比 d 长,前3个字符相同,返回长度差 5-3=2)
System.out.println("a.compareTo(a): " + a.compareTo(a)); // 0(内容一致)
// 忽略大小写的字典序比较:compareToIgnoreCase()
System.out.println("a.compareToIgnoreCase(c): " + a.compareToIgnoreCase(c)); // 0(忽略大小写,内容一致)
// 实际场景:字符串数组排序
String[] fruits = {"banana", "apple", "cherry", "date"};
Arrays.sort(fruits); // 依赖 compareTo() 排序
System.out.println("排序后:" + Arrays.toString(fruits)); // [apple, banana, cherry, date]
}
}
结论:
- 适用于 字符串排序 或需要按字典序判断大小的场景。
compareToIgnoreCase()是其忽略大小写的变体,同样返回整数。
三、常见误区与注意事项
-
误区 1:用
==比较字符串内容错误:if (s1 == s2)(仅比较地址)正确:if (s1.equals(s2))(比较内容) -
误区 2:忽略空值判断导致空指针错误:
String s = null; s.equals("abc");(直接报错)正确:"abc".equals(s)或Objects.equals(s, "abc")(JDK 7+ 工具类,空值安全) -
误区 3:认为
new String("abc")与 "abc" 引用相同new String("abc")强制在堆中创建新对象,即使常量池已有 "abc",引用也不同(需用equals()比较内容)。 -
误区 4:
compareTo()返回值仅关注正负,不关注具体数值无需纠结返回值是 1 还是 32,只需判断正负(表示顺序)或 0(表示相等)。
四、总结:比较方式选择指南
| 比较需求 | 推荐方法 | 注意事项 |
|---|---|---|
| 比较字符串内容(区分大小写) | equals() |
空值安全写法:非空串调用 |
| 比较字符串内容(忽略大小写) | equalsIgnoreCase() |
不忽略空格、特殊字符 |
| 判断两个变量是否同一对象 | == |
不用于内容比较 |
| 字符串排序 / 字典序比较 | compareTo()/compareToIgnoreCase() |
返回整数表示顺序关系 |
通过以上实例可明确:Java 字符串比较的核心是 先明确需求是 “内容” 还是 “引用”,再选择对应方法,其中
equals() 和 compareTo() 是开发中最常用的场景,需重点掌握。
浙公网安备 33010602011771号