📚 目录
🔥 字符串的不可变性
不可变设计原理
public final class String implements Serializable, Comparable<String>, CharSequence { private final char value[]; // JDK8及之前 private final byte[] value; // JDK9及之后(Compact Strings优化) }
-
final修饰:类不可继承,字符数组不可修改
-
线程安全:天然的不可变特性保证线程安全
-
哈希值缓存:首次调用hashCode()会缓存结果
-
复用优化:节省内存空间,提升性能
修改字符串的代价
String str = "Hello"; str += " World"; // 产生3个对象:原对象、新字符串、StringBuilder对象
💧 字符串常量池(String Pool)
创建机制对比
创建方式 | 示例 | 内存分配位置 | 是否触发驻留 |
---|---|---|---|
双引号直接赋值 | String s = "java"; | 字符串常量池 | 是 |
new关键字创建 | String s = new String("java"); | 堆内存 | 否(除非调用intern) |
intern方法 | s.intern(); | 可能写入常量池 | 是 |
JDK版本变化
-
JDK7+:字符串池从PermGen移动到堆内存
-
JDK8+:Metaspace取代PermGen
⚡ 高效的字符串操作
性能对比表格
特性 | String | StringBuilder | StringBuffer |
---|---|---|---|
可变性 | 不可变 | 可变 | 可变 |
线程安全 | 安全 | 不安全 | 安全 |
性能 | 连续拼接最差 | 最快 | 较快 |
容量扩展策略 | 不适用 | 默认扩容50% | 同左 |
性能测试示例代码
// 测试10000次拼接耗时 String concatWithPlus = ""; for (int i = 0; i < 10000; i++) { concatWithPlus += i; // 时间复杂度O(n²) } StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10000; i++) { sb.append(i); // 时间复杂度O(n) }
🛠️ 字符串拼接的底层秘密
编译原理分析
String result = "a" + "b" + 123; // 编译优化后: String result = "ab123"; // 变量拼接情况 String a = "Hello"; String b = "World"; String c = a + b; // 实际转换为: String c = new StringBuilder().append(a).append(b).toString();
不同类型拼接规则
类型 | 转换方式 | 示例 |
---|---|---|
基础类型 | 自动装箱 + toString() | "num:" + 5 |
null值 | 转为"null"字符串 | "val:" + null |
对象类型 | 调用对象的toString()方法 | `"obj:" + new Date() |
🚀 JDK新特性
1. 文本块(Text Blocks)JDK13+
String json = """ { "name": "Alice", "age": 25, "hobbies": ["coding", "reading"] } """;
2. 字符串模板(预览特性)JDK21+
String name = "Joan"; String info = STR."My name is \{name}";
💡 内存优化技巧
1. 重复字面量优化
String s1 = "fly"; // 放入常量池 String s2 = "fly"; // 复用池中对象 String s3 = new String("fly"); // 创建2个对象
2. 大字符串处理
// 避免巨型String对象 String hugeString = getHugeString(); String sub = new String(hugeString.substring(0,10)); // 切断引用 // 使用char数组处理敏感数据 char[] password = {'s','e','c','r','e','t'}; Arrays.fill(password, ' '); // 安全清除
⚠️ 常见陷阱
1. == vs equals
String s1 = new String("java"); String s2 = s1.intern(); System.out.println(s1 == s2); // false System.out.println(s1.equals(s2)); // true
2. 隐式内存泄漏
List<String> list = new ArrayList<>(); for (int i=0; i<100000; i++) { String temp = new String("data"); // 产生大量重复对象 list.add(temp.intern()); // 不当使用intern导致常量池膨胀 }
🏆 最佳实践
-
选择正确的字符容器:
-
单线程修改用
StringBuilder
-
多线程环境用
StringBuffer
-
配置字符串用
String
-
-
资源节约原则:
-
优先复用字符串常量
-
及时清空敏感数据
-
合理设置初始容量:
new StringBuilder(1024)
-
-
防御式编程:
public void processText(String input) { if (input == null || input.isEmpty()) { throw new IllegalArgumentException(); } String safeCopy = new String(input); // 防止原字符串被修改 }
-
性能监控工具:
-
使用JProfiler分析字符串分配
-
MAT工具查找重复字符串
-