Java 实例 – 字符串比较

在 Java 中,字符串比较是高频操作,核心需区分 内容比较 和 引用比较,避免因误用 == 和 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. 误区 1:用 == 比较字符串内容
     
    错误:if (s1 == s2)(仅比较地址)
     
    正确:if (s1.equals(s2))(比较内容)
  2. 误区 2:忽略空值判断导致空指针
     
    错误:String s = null; s.equals("abc");(直接报错)
     
    正确:"abc".equals(s) 或 Objects.equals(s, "abc")(JDK 7+ 工具类,空值安全)
  3. 误区 3:认为 new String("abc") 与 "abc" 引用相同
     
    new String("abc") 强制在堆中创建新对象,即使常量池已有 "abc",引用也不同(需用 equals() 比较内容)。
  4. 误区 4:compareTo() 返回值仅关注正负,不关注具体数值
     
    无需纠结返回值是 1 还是 32,只需判断正负(表示顺序)或 0(表示相等)。

四、总结:比较方式选择指南

比较需求推荐方法注意事项
比较字符串内容(区分大小写) equals() 空值安全写法:非空串调用
比较字符串内容(忽略大小写) equalsIgnoreCase() 不忽略空格、特殊字符
判断两个变量是否同一对象 == 不用于内容比较
字符串排序 / 字典序比较 compareTo()/compareToIgnoreCase() 返回整数表示顺序关系
 
通过以上实例可明确:Java 字符串比较的核心是 先明确需求是 “内容” 还是 “引用”,再选择对应方法,其中 equals() 和 compareTo() 是开发中最常用的场景,需重点掌握。

posted on 2025-11-27 09:38  coding博客  阅读(57)  评论(0)    收藏  举报