在Java开发中,字符串是比较操作中最常见的场景之一,无论是用户登录时的密码校验、数据排序时的字典序比较,还是文本处理中的大小写匹配,字符串比较都是绕不开的基础技能,很多初学者(甚至部分有经验的开发者)对字符串比较的底层逻辑和最佳实践仍存在模糊认知,本文将结合极客在线教育的实战教学经验,从基础到进阶,系统梳理Java中字符串比较的各种方法、使用场景及避坑指南。

为什么字符串比较容易“踩坑”?——先理解字符串的本质

要掌握字符串比较,首先要明白Java中字符串的两大核心特性:不可变性(Immutable)常量池机制

String类被设计为不可变,这意味着一旦字符串被创建,其内容就不能修改。String s = "hello"String t = new String("hello")相同,但它们的内存地址可能不同(t会在堆中新建对象,而s可能指向常量池中的字符串),这种特性直接影响了字符串比较的结果——比较的是“内容”还是“地址”?

字符串比较的“两大阵营”:“==” vs “equals()”

“==”:比较的是内存地址,不是内容!

是Java中的关系运算符,用于比较两个变量的内存地址是否相同,对于字符串来说,只有当两个字符串变量指向同一个内存对象时,才返回true

String s1 = "hello";  // 常量池中创建
String s2 = "hello";  // 直接引用常量池中的"hello"
String s3 = new String("hello");  // 堆中新建对象,内容与s1相同
String s4 = s1;       // 与s1指向同一对象
System.out.println(s1 == s2);  // true(常量池中同一个对象)
System.out.println(s1 == s3);  // false(s1在常量池,s3在堆中)
System.out.println(s1 == s4);  // true(s4是s1的引用)

极客在线教育提醒绝对不要用比较字符串内容! 这是新手最常犯的错误,用户输入的密码"123"和预设密码"123"相同,也可能返回false(因为new String()会创建新对象)。

equals():比较字符串内容是否相同

String类重写了Object类的equals()方法,专门用于比较两个字符串的是否一致(逐字符比较,区分大小写)。

String s1 = "hello";
String s2 = "Hello";
String s3 = new String("hello");
System.out.println(s1.equals(s2));  // false('h'和'H'大小写不同)
System.out.println(s1.equals(s3));  // true(内容相同,忽略地址)

核心逻辑equals()会逐个字符比较Unicode值,只有所有字符都相同(包括大小写),才返回true

equalsIgnoreCase():忽略大小写的内容比较

如果需要比较字符串内容但忽略大小写(例如用户名不区分大小写登录),可以使用equalsIgnoreCase()

String username1 = "Geek";
String username2 = "geek";
String username3 = "GEEK";
System.out.println(username1.equalsIgnoreCase(username2));  // true
System.out.println(username1.equalsIgnoreCase(username3));  // true

进阶比较:字典序与“差值”比较——compareTo()系列

除了简单的“相同/不同”,有时我们需要对字符串进行排序或比较大小(例如按字典序排列单词),这时就需要用到compareTo()方法。

compareTo():字典序比较,返回差值

compareTo()按字典序(lexicographical order)比较两个字符串,返回一个整数值:

  • 返回0:两个字符串内容完全相同(相当于equals()返回true);
  • 返回负数:调用字符串的字典序小于参数字符串;
  • 返回正数:调用字符串的字典序大于参数字符串。

字典序规则:从左到右逐个字符比较Unicode值,第一个不同字符的差值即为结果,如果其中一个字符串是另一个的前缀,则较短的字符串更小。

String s1 = "apple";
String s2 = "banana";
String s3 = "apple";
String s4 = "applet";
System.out.println(s1.compareTo(s2));  // -1('a'的Unicode是97,'b'是98,97-98=-1)
System.out.println(s2.compareTo(s1));  // 1
System.out.println(s1.compareTo(s3));  // 0
System.out.println(s1.compareTo(s4));  // -4("apple"比"applet"短4个字符)

compareToIgnoreCase():忽略大小写的字典序比较

类似equalsIgnoreCase()compareToIgnoreCase()在比较时忽略大小写,返回值规则与compareTo()一致。

String s1 = "Geek";
String s2 = "geek";
String s3 = "Google";
System.out.println(s1.compareToIgnoreCase(s2));  // 0(忽略大小写后内容相同)
System.out.println(s1.compareToIgnoreCase(s3));  // 6('G'和'g'的Unicode相同,'e'和'o'差6)

安全比较:Objects.equals()Objects.requireNonNull()

在实际开发中,字符串变量可能为null,直接调用equals()会导致NullPointerException,Java 7引入的Objects类提供了更安全的比较方法。

Objects.equals(a, b):安全的“内容比较”

Objects.equals()会先检查两个参数是否为null

  • 如果ab都为null,返回true
  • 如果其中一个为null,返回false
  • 否则调用a.equals(b)
String s1 = null;
String s2 = "hello";
String s3 = null;
System.out.println(s1.equals(s2));  // NPE!
System.out.println(Objects.equals(s