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。拼接少量字符串时直接用 + 即可,不必为微小优化牺牲可读性。
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。
自测
- 两个内容相同的
String一定能用==判断相等吗?不能,用equals()。 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 字节数;再写一个对象数组复制例子解释浅复制。