Java中String类
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); // 输出: HelloSystem.out.println("str2: " + str2); // 输出: Hello WorldSystem.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()); // 16System.out.println("是否为空: " + str.isEmpty()); // falseSystem.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")); // trueSystem.out.println("以'Java'开头: " + text.startsWith("Java")); // trueSystem.out.println("以'powerful'结尾: " + text.endsWith("powerful")); // true// 索引查找System.out.println("'Java'首次出现位置: " + text.indexOf("Java")); // 0System.out.println("'Java'最后出现位置: " + text.lastIndexOf("Java")); // 15System.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:少量字符串操作 - 使用Stringpublic String formatUserInfo(String name, int age) {return "用户: " + name + ", 年龄: " + age; // 简单拼接,使用String}// 场景2:多线程环境下的字符串构建 - 使用StringBufferpublic 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:单线程环境下的大量字符串操作 - 使用StringBuilderpublic 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 == s3System.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;}// ✅ 正确做法:使用StringBuilderpublic 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(); // 默认容量16for (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情况}// ✅ 常量放在前面,避免NullPointerExceptionpublic 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);}
}