Java 字符串

字符串是 Java 中用得最多的引用类型——日志、接口、配置、用户输入,到处都是 String。本章带你彻底搞懂 Java 字符串的核心:不可变性、常用方法、StringBuilder、String.format,以及新手最常踩的 == vs equals 陷阱。

1. 创建字符串:& == vs equals 的陷阱

String 是引用类型,创建方式有几种。最关键的是理解字符串常量池== vs equals 的区别:

public class StringCreate {
    public static void main(String[] args) {
        // 1. 直接量(最常用,放进字符串常量池)
        String s1 = "Hello";

        // 2. new 创建(在堆上新建对象)
        String s2 = new String("Hello");

        // 3. 从字符数组构造
        char[] chars = {'J', 'a', 'v', 'a'};
        String s3 = new String(chars);

        // 4. 从字节数组构造(网络/文件场景常用)
        byte[] bytes = {72, 105};
        String s4 = new String(bytes);    // "Hi"

        System.out.println(s1 + " " + s2 + " " + s3 + " " + s4);

        // ⚠️ String 是引用类型,但 == 比较的是地址,不是内容!
        String a = "hi";
        String b = "hi";
        String c = new String("hi");
        System.out.println(a == b);           // true (常量池复用)
        System.out.println(a == c);           // false(堆上新建)
        System.out.println(a.equals(c));      // true (内容相同)
    }
}

新手雷区:== 比较的是地址(两个引用是否指向同一对象),equals 比较的是内容。字符串内容比较永远用 .equals()!IDEA 会在 str1 == str2 处报警告,要养成习惯。

2. String 的不可变性(Immutable)

Java 中 String 对象一旦创建,内容就不可变。所有"修改"方法(replacesubstringtoLowerCase)都是返回新字符串,不改原对象:

public class Immutable {
    public static void main(String[] args) {
        String s = "Hello";
        String s2 = s.replace("H", "J");
        System.out.println(s);     // Hello(原字符串不变!)
        System.out.println(s2);    // Jello
        // s 和 s2 是两个不同的对象

        // 字符串拼接也是创建新对象
        String a = "a" + "b" + "c";   // 编译期优化成 "abc"
        // 循环里拼接会创建大量中间对象,这就是为什么要用 StringBuilder
    }
}

不可变的好处:线程安全(多线程放心共享)、可以做 Map 的 key(hashCode 不会变)、常量池复用省内存。代价是大量拼接时性能差——下面用 StringBuilder 解决。

3. 常用方法(必须熟练)

String 有几十个方法,日常 80% 用到的是下面这些:

public class StringMethods {
    public static void main(String[] args) {
        String s = "Hello, Java";

        // 长度
        System.out.println(s.length());         // 11

        // 取字符
        System.out.println(s.charAt(0));        // H

        // 子串:beginIndex(包含) 到 endIndex(不包含)
        System.out.println(s.substring(7));     // Java
        System.out.println(s.substring(0, 5));  // Hello

        // 查找
        System.out.println(s.indexOf("Java"));  // 7
        System.out.println(s.indexOf("x"));     // -1(找不到)
        System.out.println(s.contains("Java")); // true

        // 大小写
        System.out.println(s.toUpperCase());    // HELLO, JAVA
        System.out.println(s.toLowerCase());    // hello, java

        // 替换
        System.out.println(s.replace("Java", "World"));  // Hello, World

        // 去掉首尾空白
        String padded = "  hi  ";
        System.out.println(padded.trim());      // "hi"
        System.out.println(padded.strip());     // Java 11+,支持 Unicode 空白

        // 分割
        String csv = "a,b,c,d";
        String[] parts = csv.split(",");
        for (String p : parts) System.out.println(p);   // a b c d

        // 比较时忽略大小写
        System.out.println("ABC".equalsIgnoreCase("abc"));   // true

        // 判空
        System.out.println(s.isEmpty());        // false(length == 0)
        System.out.println(s.isBlank());        // false(Java 11+,全空白也算空)
    }
}

记忆技巧:Java 的 length()方法(带括号),而数组的 length属性(不带括号)——这是常见混淆点。

4. StringBuilder(循环拼接必用)

在循环里用 + 拼接字符串性能很差(每次都新建对象),正确做法是用 StringBuilder:

public class StringBuilderDemo {
    public static void main(String[] args) {
        // ⚠️ 在循环里拼接字符串,必须用 StringBuilder!
        // 直接用 + 每次都新建对象,性能差

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 5; i++) {
            sb.append("item").append(i).append(" ");
        }
        String result = sb.toString();
        System.out.println(result);     // item0 item1 item2 item3 item4

        // StringBuilder 常用方法
        StringBuilder b = new StringBuilder("Hello");
        b.append(", World");          // 追加
        b.insert(5, " Java");         // 在索引处插入
        b.delete(5, 10);              // 删除 [5, 10)
        b.reverse();                  // 反转
        System.out.println(b);        // dlroW ,olleH

        // 链式调用:每个方法都返回 this
        String chained = new StringBuilder()
            .append("A").append("B").append("C")
            .toString();
        System.out.println(chained);   // ABC
    }
}

String vs StringBuilder vs StringBuffer:

实际开发 99% 用 StringBuilder,多线程共享字符串的场景极少。

5. 字符串与基本类型互转

这是处理用户输入、解析配置时的核心操作:

public class StringConvert {
    public static void main(String[] args) {
        // String -> 基本类型:用包装类的 parseXxx
        int i = Integer.parseInt("123");
        double d = Double.parseDouble("3.14");
        boolean b = Boolean.parseBoolean("true");
        System.out.println(i + " " + d + " " + b);

        // 基本类型 -> String:三种方式
        String s1 = String.valueOf(123);     // 推荐
        String s2 = Integer.toString(123);
        String s3 = 123 + "";                // 最简单但可读性差

        // ⚠️ parseInt 遇到非数字会抛 NumberFormatException
        try {
            int n = Integer.parseInt("abc");    // ❌ 抛异常
        } catch (NumberFormatException e) {
            System.out.println("格式错误: " + e.getMessage());
        }
    }
}

记住两个常用 API:Integer.parseInt(s)(字符串转 int)、String.valueOf(x)(任意类型转字符串)。parseInt 失败抛 NumberFormatException,生产代码要捕获。

6. 字符串格式化

需要把多个变量拼到模板字符串里时,用 String.formatSystem.out.printf:

public class StringFormat {
    public static void main(String[] args) {
        String name = "小明";
        int age = 20;
        double score = 95.5;

        // String.format:返回格式化字符串
        String s1 = String.format("姓名: %s, 年龄: %d", name, age);
        System.out.println(s1);    // 姓名: 小明, 年龄: 20

        // 常用占位符:
        // %s 字符串   %d 整数   %f 浮点   %c 字符   %b 布尔
        // %n 换行     %% 百分号本身

        // 控制宽度和精度
        System.out.println(String.format("分数: %.2f", score));     // 95.50
        System.out.println(String.format("|%10d|", 42));           // |        42|(右对齐 10 列)
        System.out.println(String.format("|%-10d|", 42));          // |42        |(左对齐)

        // System.out.printf = print + format 一步到位
        System.out.printf("姓名: %s, 分数: %.1f%n", name, score);
    }
}

7. 字符串编码(进阶)

计算机只认数字,字符要靠编码(UTF-8、GBK)映射成字节。Java 内部用 UTF-16 存字符。读写文件、网络传输时要注意编码:

小结

这一章你掌握了 Java 字符串的全部核心:创建、不可变性、常用方法、StringBuilder、类型转换、格式化。新手记住三件事:内容比较用 equals循环拼接用 StringBuilderparseInt 要捕获异常。下一篇我们终于进入 Java 的灵魂——类与对象。

← 上一篇 Java 数组

下一篇 Java 类与对象

✈️💬