← Backend / Java

12_Java 字符串与数组

String 内容比较、拼接、Unicode、数组复制与定长容器的使用边界。

Java 字符串与数组

学习目标:正确处理字符串比较、拼接、Unicode 和定长数组,避免把引用相等当成内容相等。

1. String 是不可变对象

String 创建后内容不可改;拼接会产生新的字符串。== 比较引用是否指向同一对象,equals() 比较内容。

public class Main {
    public static void main(String[] args) {
        String a = new String("hello");
        String b = "hello";
        System.out.println(a == b);       // false
        System.out.println(a.equals(b));  // true
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 3; i++) builder.append(i);
        System.out.println(builder);      // 012
    }
}

循环中大量拼接优先考虑 StringBuilder。拼接少量字符串时直接用 + 即可,不必为微小优化牺牲可读性。

易错点:`text.length()` 返回 UTF-16 代码单元数,不总等于用户看到的字符数。处理 Unicode 码点可用 `codePointCount`;字形簇还需要更高层的文本处理。

2. 空值与格式化

调用可能为 null 的变量的方法会抛 NullPointerException。若业务允许空值,应在边界明确处理;若不允许,尽早校验。String.format 或文本块适合有明确格式的输出,构造 SQL 则必须使用参数化查询,不能靠字符串拼接。

3. 数组是定长容器

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] scores = {90, 80, 70};
        scores[1] = 85;
        System.out.println(scores.length);        // 3
        System.out.println(Arrays.toString(scores));
        int[] copy = Arrays.copyOf(scores, scores.length);
        System.out.println(Arrays.equals(scores, copy)); // true
    }
}

数组长度固定,索引从 0 开始。Arrays.copyOf 复制一维基本类型数组的元素;对象数组复制的是引用,嵌套对象仍可能共享。需要动态长度时通常使用 List。

自测

  1. 两个内容相同的 String 一定能用 == 判断相等吗?不能,用 equals()。
  2. Arrays.copyOf 是否会深复制对象图?不会。

4. 进一步理解字符串相等与编码

字符串常量可能被驻留(intern),因此某些 == 比较偶然返回 true,这并不能作为内容比较规则。调用 a.equals(b) 需要保证 a 非空;若允许空值,可用 Objects.equals(a, b)。用户名、文件名等比较还可能涉及大小写折叠和 Unicode 规范化,不能简单把 equalsIgnoreCase 当作所有语言环境的业务规则。

import java.nio.charset.StandardCharsets;
import java.util.Objects;

class Example {
    static boolean same(String a, String b) { return Objects.equals(a, b); }
    static int utf8Bytes(String text) {
        return text.getBytes(StandardCharsets.UTF_8).length;
    }
}

length() 是 UTF-16 代码单元数,codePointCount(0, text.length()) 是码点数,UTF-8 编码后的 byte[] 长度是字节数;三者服务于不同需求。数据库字段或 HTTP 限制若按字节计算,应显式计算字节,不能直接用 length()。

5. 数组边界与复制

int[] 存基本类型值,String[] 存对象引用。Arrays.copyOf 对对象数组只复制引用;改变复制数组中的元素位置不会改原数组,但修改两个数组共同指向的可变对象会同时可见。比较一维数组内容用 Arrays.equals,多维嵌套数组可用 Arrays.deepEquals,而 array1 == array2 比较的是数组对象身份。

练习:分别输出 "你好".length()、码点数和 UTF-8 字节数;再写一个对象数组复制例子解释浅复制。