Java中的String详解
1. String类核心概念
1.1 不可变性(Immutability)
String类是不可变的(Immutable),这是其最重要的特性:
public final class String implements Serializable, Comparable<String>, CharSequence {
// 字符数组被final修饰,且没有提供修改方法
private final char[] value; // JDK 8
// private final byte[] value; // JDK 9+
}
不可变性的实现原理:
String类被final修饰,不能被继承- 内部字符数组
value被final修饰 - 没有提供任何修改内部状态的方法
- 所有"修改"操作都返回新的String对象
示例代码:
public class StringImmutableDemo {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = str1.concat(" World");
System.out.println("str1: " + str1); // 输出: Hello
System.out.println("str2: " + str2); // 输出: Hello World
System.out.println("str1 == str2: " + (str1 == str2)); // false
// str1的值没有改变,concat()返回了新的String对象
}
}
1.2 不可变性的优势
- 线程安全:多线程环境下无需同步
- 缓存HashCode:String的hashCode只需计算一次
- 字符串常量池:相同内容的字符串可以共享内存
- 安全性:防止恶意修改,适合作为HashMap的key
2. String的内存模型
2.1 字符串存储位置
public class StringMemoryDemo {
public static void main(String[] args) {
// 1. 字符串字面量 - 存储在字符串常量池
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // true - 指向同一个对象
// 2. new关键字 - 在堆中创建新对象
String str3 = new String("Hello");
String str4 = new String("Hello");
System.out.println(str3 == str4); // false - 不同对象
System.out.println(str1 == str3); // false - 不同位置
// 3. 内容比较
System.out.println(str1.equals(str3)); // true - 内容相同
}
}
2.2 JVM内存分布
3. String常用方法详解
3.1 字符串创建和基本操作
public class StringMethodsDemo {
public static void main(String[] args) {
String str = "Hello World Java";
// 长度和判空
System.out.println("长度: " + str.length()); // 16
System.out.println("是否为空: " + str.isEmpty()); // false
System.out.println("是否为空白: " + str.isBlank()); // false (JDK 11+)
// 字符访问
System.out.println("第6个字符: " + str.charAt(5)); // 'W'
System.out.println("字符数组: " + Arrays.toString(str.toCharArray()));
// 子字符串
System.out.println("子串(6,11): " + str.substring(6, 11)); // "World"
System.out.println("从索引6开始: " + str.substring(6)); // "World Java"
}
}
3.2 字符串查找和匹配
public class StringSearchDemo {
public static void main(String[] args) {
String text = "Java is great, Java is powerful";
// 查找方法
System.out.println("包含'great': " + text.contains("great")); // true
System.out.println("以'Java'开头: " + text.startsWith("Java")); // true
System.out.println("以'powerful'结尾: " + text.endsWith("powerful")); // true
// 索引查找
System.out.println("'Java'首次出现位置: " + text.indexOf("Java")); // 0
System.out.println("'Java'最后出现位置: " + text.lastIndexOf("Java")); // 15
System.out.println("从索引5开始查找'is': " + text.indexOf("is", 5)); // 5
// 正则匹配
System.out.println("匹配模式: " + text.matches(".*Java.*")); // true
}
}
3.3 字符串转换和处理
public class StringTransformDemo {
public static void main(String[] args) {
String original = " Hello World Java ";
// 大小写转换
System.out.println("大写: " + original.toUpperCase());
System.out.println("小写: " + original.toLowerCase());
// 去除空白
System.out.println("去除首尾空白: '" + original.trim() + "'");
System.out.println("去除所有空白: '" + original.strip() + "'"); // JDK 11+
// 替换操作
System.out.println("替换空格为'-': " + original.replace(" ", "-"));
System.out.println("替换首个'l': " + original.replaceFirst("l", "L"));
System.out.println("正则替换: " + original.replaceAll("\\s+", "_"));
// 分割
String[] words = original.trim().split("\\s+");
System.out.println("分割结果: " + Arrays.toString(words));
}
}
4. String、StringBuffer、StringBuilder对比
4.1 核心特性对比
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 是 | 是(同步方法) | 否 |
| 性能 | 低(频繁操作时) | 中等 | 高 |
| 内存开销 | 高(创建新对象) | 低 | 低 |
| 适用场景 | 少量字符串操作 | 多线程环境 | 单线程环境 |
4.2 详细特性分析
4.2.1 String类
public class StringAnalysis {
public static void main(String[] args) {
// String的不可变性导致每次操作都创建新对象
String str = "Hello";
str = str + " World"; // 创建新的String对象
str = str + " Java"; // 再次创建新对象
// 线程安全:由于不可变性,天然线程安全
// 适用场景:字符串内容不频繁变化的情况
}
}
4.2.2 StringBuffer类
public class StringBufferAnalysis {
public static void main(String[] args) {
// StringBuffer是可变的,线程安全
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World"); // 在原对象上修改,不创建新对象
sb.append(" Java"); // 继续在原对象上修改
// 所有方法都是synchronized的
// 适用场景:多线程环境下的字符串操作
System.out.println(sb.toString());
}
}
4.2.3 StringBuilder类
public class StringBuilderAnalysis {
public static void main(String[] args) {
// StringBuilder是可变的,非线程安全,性能最好
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 在原对象上修改
sb.append(" Java"); // 性能最优
// 方法不是synchronized的,性能更好
// 适用场景:单线程环境下的字符串操作
System.out.println(sb.toString());
}
}
4.3 使用场景总结
public class StringUsageScenarios {
// 场景1:少量字符串操作 - 使用String
public String formatUserInfo(String name, int age) {
return "用户: " + name + ", 年龄: " + age; // 简单拼接,使用String
}
// 场景2:多线程环境下的字符串构建 - 使用StringBuffer
public class LogBuffer {
private StringBuffer buffer = new StringBuffer();
public synchronized void addLog(String message) {
buffer.append(System.currentTimeMillis())
.append(": ")
.append(message)
.append("\n");
}
public synchronized String getLogs() {
return buffer.toString();
}
}
// 场景3:单线程环境下的大量字符串操作 - 使用StringBuilder
public String generateReport(List<String> data) {
StringBuilder report = new StringBuilder();
report.append("=== 报告开始 ===\n");
for (String item : data) {
report.append("- ").append(item).append("\n");
}
report.append("=== 报告结束 ===");
return report.toString();
}
}
5. 字符串常量池与intern()方法
5.1 字符串常量池机制
字符串常量池(String Pool)是JVM为了优化字符串存储而设计的特殊内存区域:
public class StringPoolDemo {
public static void main(String[] args) {
// 字面量直接进入常量池
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true - 指向同一个对象
// new创建的对象在堆中
String s3 = new String("hello");
System.out.println(s1 == s3); // false - 不同位置
// 编译时常量拼接会进入常量池
String s4 = "hel" + "lo";
System.out.println(s1 == s4); // true - 编译时优化
// 运行时拼接不会进入常量池
String prefix = "hel";
String s5 = prefix + "lo";
System.out.println(s1 == s5); // false - 运行时创建
}
}
5.2 intern()方法详解
intern()方法用于手动将字符串加入常量池:
public class InternMethodDemo {
public static void main(String[] args) {
// 基本用法
String s1 = new String("hello");
String s2 = s1.intern(); // 将s1的内容加入常量池
String s3 = "hello";
System.out.println(s1 == s2); // false - s1在堆中,s2指向常量池
System.out.println(s2 == s3); // true - 都指向常量池
// intern()的返回值
String s4 = new String("world");
String s5 = s4.intern();
String s6 = s4.intern();
System.out.println(s5 == s6); // true - intern()返回常量池中的引用
// 性能考虑
demonstrateInternPerformance();
}
private static void demonstrateInternPerformance() {
long startTime = System.currentTimeMillis();
// 大量使用intern()可能导致性能问题
for (int i = 0; i < 100000; i++) {
String s = new String("test" + i).intern();
}
long endTime = System.currentTimeMillis();
System.out.println("intern()操作耗时: " + (endTime - startTime) + "ms");
}
}
5.3 JDK版本差异
public class InternVersionDifference {
public static void main(String[] args) {
// JDK 6 vs JDK 7+ 的区别
String s1 = new String("1") + new String("1");
String s2 = s1.intern();
String s3 = "11";
// JDK 6: s1 != s2, s2 == s3
// JDK 7+: s1 == s2, s2 == s3
System.out.println("s1 == s2: " + (s1 == s2));
System.out.println("s2 == s3: " + (s2 == s3));
/*
* JDK 6: 常量池在永久代,intern()会复制对象到常量池
* JDK 7+: 常量池在堆中,intern()可能只存储引用
*/
}
}
6. 性能优化最佳实践
6.1 避免在循环中进行字符串拼接
public class StringOptimization {
// ❌ 错误做法:在循环中使用String拼接
public String badConcatenation(List<String> items) {
String result = "";
for (String item : items) {
result += item + ","; // 每次都创建新对象
}
return result;
}
// ✅ 正确做法:使用StringBuilder
public String goodConcatenation(List<String> items) {
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item).append(",");
}
return sb.toString();
}
// ✅ 更好的做法:使用String.join() (JDK 8+)
public String bestConcatenation(List<String> items) {
return String.join(",", items);
}
}
6.2 合理使用StringBuilder的容量
public class StringBuilderOptimization {
// ❌ 未指定初始容量,可能频繁扩容
public String inefficientBuilder(List<String> items) {
StringBuilder sb = new StringBuilder(); // 默认容量16
for (String item : items) {
sb.append(item);
}
return sb.toString();
}
// ✅ 预估容量,减少扩容次数
public String efficientBuilder(List<String> items) {
int estimatedLength = items.size() * 10; // 预估总长度
StringBuilder sb = new StringBuilder(estimatedLength);
for (String item : items) {
sb.append(item);
}
return sb.toString();
}
}
6.3 字符串比较优化
public class StringComparisonOptimization {
// ✅ 使用equals()进行内容比较
public boolean safeEquals(String s1, String s2) {
return Objects.equals(s1, s2); // 处理null情况
}
// ✅ 常量放在前面,避免NullPointerException
public boolean constantFirst(String input) {
return "ADMIN".equals(input); // 而不是 input.equals("ADMIN")
}
// ✅ 使用equalsIgnoreCase()进行忽略大小写比较
public boolean caseInsensitiveEquals(String s1, String s2) {
return s1 != null && s1.equalsIgnoreCase(s2);
}
}
6.4 JDK 8+ 字符串优化特性
public class ModernStringOptimization {
// JDK 8: 编译器自动优化字符串拼接
public String compilerOptimization() {
String name = "张三";
int age = 25;
// 编译器会自动转换为StringBuilder操作
return "姓名: " + name + ", 年龄: " + age;
}
// JDK 8+: 使用String.join()
public String joinStrings(String... parts) {
return String.join(" - ", parts);
}
// JDK 11+: 使用新的字符串方法
public void modernStringMethods() {
String text = " Hello World ";
// 重复字符串
String repeated = "=".repeat(10); // "=========="
// 判断空白
boolean isBlank = text.isBlank();
// 去除空白(比trim()更强大)
String stripped = text.strip();
// 按行分割
text.lines().forEach(System.out::println);
}
}

被折叠的 条评论
为什么被折叠?



