Java String类详解:不可变性、核心方法与性能优化
2026/9/15 0:25:57 网站建设 项目流程

1. Java String类基础解析

String类是Java语言中最基础也最常用的类之一,它代表不可变的字符序列。在Java中,所有用双引号括起来的字符串(如"Hello World")都是String类的实例。

1.1 String类的不可变性

String对象一旦创建,其内容就不能被改变。这种设计带来了几个重要特性:

  • 线程安全:由于不可变,String对象可以被多个线程安全共享
  • 缓存哈希值:String类缓存了hashCode()的计算结果,因为内容不变,哈希值也不会变
  • 字符串池优化:JVM可以使用字符串池来存储字符串字面量,节省内存
String s1 = "hello"; String s2 = s1.concat(" world"); // 创建新对象,s1不变 System.out.println(s1); // 输出"hello" System.out.println(s2); // 输出"hello world"

1.2 String的创建方式

Java中创建String对象主要有两种方式:

  1. 字面量方式:直接使用双引号创建
    String s1 = "hello";
  2. new关键字方式:通过构造函数创建
    String s2 = new String("hello");

这两种方式在内存分配上有重要区别:

  • 字面量方式会检查字符串池,如果存在则直接引用,否则创建新对象并放入池中
  • new方式每次都会在堆中创建新对象,不检查字符串池

2. String类的核心方法详解

2.1 字符串比较方法

String类提供了多种比较方法,各有不同的用途:

  1. equals():比较字符串内容是否相同

    String s1 = "hello"; String s2 = "HELLO"; System.out.println(s1.equals(s2)); // false
  2. equalsIgnoreCase():忽略大小写比较内容

    System.out.println(s1.equalsIgnoreCase(s2)); // true
  3. compareTo():按字典顺序比较字符串

    System.out.println("apple".compareTo("banana")); // 负数
  4. ==运算符:比较对象引用是否相同

    String s3 = "hello"; String s4 = "hello"; String s5 = new String("hello"); System.out.println(s3 == s4); // true System.out.println(s3 == s5); // false

2.2 字符串查找方法

String类提供了丰富的查找方法:

  1. indexOf():查找字符或子串首次出现的位置

    String str = "Hello World"; System.out.println(str.indexOf('o')); // 4 System.out.println(str.indexOf("World")); // 6
  2. lastIndexOf():查找字符或子串最后一次出现的位置

    System.out.println(str.lastIndexOf('o')); // 7
  3. contains():检查是否包含指定字符序列

    System.out.println(str.contains("llo")); // true
  4. startsWith()/endsWith():检查字符串开头/结尾

    System.out.println(str.startsWith("Hello")); // true System.out.println(str.endsWith("ld")); // true

2.3 字符串操作与转换

  1. substring():获取子字符串

    System.out.println(str.substring(6)); // "World" System.out.println(str.substring(0, 5)); // "Hello"
  2. concat():连接字符串

    System.out.println("Hello".concat(" World")); // "Hello World"
  3. replace():替换字符或字符串

    System.out.println("hello".replace('l', 'L')); // "heLLo"
  4. toUpperCase()/toLowerCase():大小写转换

    System.out.println(str.toUpperCase()); // "HELLO WORLD"
  5. trim():去除首尾空白字符

    System.out.println(" hello ".trim()); // "hello"

3. String与正则表达式

String类提供了强大的正则表达式支持:

3.1 matches()方法

检查整个字符串是否匹配正则表达式:

String email = "test@example.com"; System.out.println(email.matches("^[\\w-]+@[\\w-]+\\.[a-z]{2,3}$")); // true

3.2 split()方法

根据正则表达式分割字符串:

String data = "apple,orange,banana"; String[] fruits = data.split(","); for (String fruit : fruits) { System.out.println(fruit); }

3.3 replaceAll()/replaceFirst()方法

使用正则表达式替换:

String text = "foo123bar456baz"; System.out.println(text.replaceAll("\\d+", "-")); // "foo-bar-baz" System.out.println(text.replaceFirst("\\d+", "-")); // "foo-bar456baz"

4. String与字符编码

4.1 编码转换

String类提供了与字节数组相互转换的方法,支持指定字符集:

String str = "你好"; byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8); String newStr = new String(utf8Bytes, StandardCharsets.UTF_8);

4.2 字符集注意事项

  • 默认使用平台字符集,可能导致跨平台问题
  • 推荐明确指定字符集,如UTF-8
  • 处理IO时,字符集不一致会导致乱码

5. String性能优化

5.1 字符串拼接的优化

  1. 使用StringBuilder进行多次拼接:

    StringBuilder sb = new StringBuilder(); for (int i = 0; i < 100; i++) { sb.append(i).append(", "); } String result = sb.toString();
  2. 避免在循环中使用+操作符拼接字符串:

    // 低效做法 String s = ""; for (int i = 0; i < 100; i++) { s += i; // 每次循环创建新String对象 }

5.2 字符串池的利用

  1. 尽量使用字面量方式创建字符串
  2. 使用intern()方法将字符串显式加入字符串池:
    String s1 = new String("hello").intern(); String s2 = "hello"; System.out.println(s1 == s2); // true

6. 常见问题与解决方案

6.1 字符串比较问题

问题:使用==比较字符串内容

String s1 = "hello"; String s2 = new String("hello"); System.out.println(s1 == s2); // false

解决方案:始终使用equals()方法比较内容

6.2 字符串不可变导致的性能问题

问题:频繁修改字符串产生大量临时对象

String s = ""; for (int i = 0; i < 10000; i++) { s += i; // 性能极差 }

解决方案:使用StringBuilder

6.3 内存泄漏问题

问题:超大字符串的子字符串保持原字符串引用

String huge = "...非常长的字符串..."; String small = huge.substring(0, 10); // small仍引用huge的char[]

解决方案(Java 7+):使用new String(small)切断引用

6.4 编码转换问题

问题:未指定字符集导致乱码

byte[] bytes = "你好".getBytes(); // 使用平台默认字符集 String s = new String(bytes, "ISO-8859-1"); // 乱码

解决方案:始终明确指定字符集

byte[] bytes = "你好".getBytes(StandardCharsets.UTF_8); String s = new String(bytes, StandardCharsets.UTF_8);

7. Java 8及以后版本的String增强

7.1 Java 8的StringJoiner

简化字符串拼接:

StringJoiner sj = new StringJoiner(", ", "[", "]"); sj.add("apple").add("orange").add("banana"); System.out.println(sj.toString()); // "[apple, orange, banana]"

7.2 Java 11的String新方法

  1. isBlank():检查字符串是否为空或仅包含空白字符

    System.out.println(" ".isBlank()); // true
  2. lines():将字符串按行分割为流

    String text = "line1\nline2\nline3"; text.lines().forEach(System.out::println);
  3. repeat():重复字符串

    System.out.println("abc".repeat(3)); // "abcabcabc"
  4. strip():去除首尾空白字符(比trim()更严格)

    System.out.println(" hello ".strip()); // "hello"

8. 实际应用案例

8.1 字符串反转

public static String reverse(String input) { return new StringBuilder(input).reverse().toString(); }

8.2 检查回文字符串

public static boolean isPalindrome(String str) { String clean = str.replaceAll("\\s+", "").toLowerCase(); return new StringBuilder(clean).reverse().toString().equals(clean); }

8.3 统计字符出现次数

public static int countChar(String str, char c) { return (int) str.chars().filter(ch -> ch == c).count(); }

8.4 字符串格式化

String formatted = String.format("Name: %s, Age: %d, Salary: %.2f", "John", 30, 5000.555); System.out.println(formatted); // "Name: John, Age: 30, Salary: 5000.56"

9. String与集合框架

9.1 字符串排序

List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie"); Collections.sort(names); // 自然顺序排序 names.sort(Comparator.comparingInt(String::length)); // 按长度排序

9.2 字符串过滤

List<String> list = Arrays.asList("apple", "banana", "cherry", "date"); List<String> filtered = list.stream() .filter(s -> s.startsWith("a")) .collect(Collectors.toList());

9.3 字符串分组

List<String> words = Arrays.asList("apple", "banana", "apricot", "berry"); Map<Character, List<String>> grouped = words.stream() .collect(Collectors.groupingBy( s -> s.charAt(0)));

10. 高级话题:String与JVM

10.1 字符串池的实现

  • JVM维护一个字符串常量池(String Pool)
  • 字面量字符串自动入池
  • 可以手动调用intern()方法入池
  • Java 7+将字符串池从永久代移到堆中

10.2 字符串的内存占用

  • 每个String对象包含:
    • 对象头(12-16字节,取决于JVM)
    • char[]数组引用(4-8字节)
    • 哈希值缓存(4字节)
  • char[]数组本身占用内存:2字节 × 字符数 + 数组对象头

10.3 字符串压缩

Java 9引入了紧凑字符串(Compact Strings):

  • 对于仅含Latin-1字符的字符串,使用byte[]而非char[]存储
  • 节省约50%内存
  • 完全透明,无需修改代码

11. 最佳实践总结

  1. 优先使用字符串字面量而非new String()
  2. 多字符串拼接使用StringBuilder
  3. 比较字符串内容始终使用equals()而非==
  4. 明确指定字符集以避免编码问题
  5. 利用Java新版本提供的字符串方法简化代码
  6. 注意字符串操作在循环中的性能影响
  7. 处理用户输入时总是进行空检查和trim()
  8. 对于大量文本处理,考虑使用更专业的类如StringBuffer(线程安全)或第三方库
  9. 了解JVM字符串优化特性,如Java 9的紧凑字符串
  10. 在性能敏感场景,考虑直接操作char[]或使用更高效的数据结构

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询