抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

Java 的 String 类是最常用的类之一,但其底层实现和特性往往容易被忽视。以下是 String 类的重点解析,涵盖核心概念、内存管理、性能优化及常见问题。

String 类的核心特点

String 是 Java 中用于处理字符串的类,位于 java.lang 包下,无需手动导入。以下是其主要特点:

不可变性(Immutability)

String 对象一旦创建,其内容无法修改。任何看似修改字符串的操作(如拼接、替换)实际上都会创建一个新的 String 对象。这种设计带来了线程安全性和内存优化的好处,但也需要注意性能问题(例如频繁拼接时建议使用 StringBuilder)。

示例:

1
2
String s1 = "Hello";
s1 = s1 + " World"; // 创建新对象,原对象未改变

字符串的创建方式与内存分配

Java 维护了一个字符串常量池,用于存储字符串字面量。例如,String s1 = "hello"; String s2 = "hello"; 中,s1s2 指向常量池中的同一个对象。这种机制减少了内存占用,但通过 new String("hello") 创建的字符串会在堆中分配新对象。

  • 字面量赋值:直接使用双引号。检查字符串常量池,存在则附庸,否则新建并缓存。
  • new 关键字:强制在堆中创建新对象,不检查常量池。

字符串常量池(String Pool)是 JVM 维护的一块特殊内存区域,存储唯一字符串字面量。

基于字符数组实现

在 JDK 8 及之前,String 内部使用 char[] 存储字符数据。从 JDK 9 开始,改为使用 byte[] 加上编码标志,以更高效地存储字符串(支持 Latin-1 和 UTF-16 编码)。

final

String 类被声明为 final,无法被继承。这种设计确保了其不可变性和安全性。

构造方法

String 类提供了多种构造方法,以下是常见的几种:

  • String():创建一个空字符串。
  • String(String original):通过已有字符串创建新字符串。
  • String(char[] value):通过字符数组创建字符串。
  • String(byte[] bytes, Charset charset):通过字节数组和指定字符集创建字符串。

示例

1
2
3
4
String s1 = new String();               // ""
String s2 = new String("hello"); // "hello"
char[] chars = {'h', 'i'};
String s3 = new String(chars); // "hi"

常用方法

String 类提供了丰富的操作方法,以下按功能分类介绍:

获取信息

  • length():返回字符串长度。
  • charAt(int index):返回指定索引处的字符(索引从 0 开始)。
  • isEmpty():判断字符串是否为空。

示例

1
2
3
4
String str = "Hello, Java";
System.out.println(str.length()); // 11
System.out.println(str.charAt(1)); // 'e'
System.out.println(str.isEmpty()); // false

比较

  • equals(Object obj):比较两个字符串内容是否相等(区分大小写)。
  • equalsIgnoreCase(String another):忽略大小写比较。
  • compareTo(String another):按字典序比较,返回正数、负数或 0。

示例

1
2
3
4
5
String s1 = "java";
String s2 = "Java";
System.out.println(s1.equals(s2)); // false
System.out.println(s1.equalsIgnoreCase(s2)); // true
System.out.println(s1.compareTo(s2)); // 32('j' - 'J' 的 ASCII 差值)

查找与定位

  • indexOf(int ch/String str):返回字符或子串首次出现的位置,未找到返回 -1。
  • lastIndexOf(int ch/String str):返回最后一次出现的位置。
  • contains(CharSequence s):判断是否包含指定子串。

示例

1
2
3
4
String str = "Hello, Java";
System.out.println(str.indexOf("a")); // 7
System.out.println(str.lastIndexOf("a")); // 9
System.out.println(str.contains("Java")); // true

修改(返回新字符串)

  • substring(int beginIndex):从指定索引截取到末尾。
  • substring(int beginIndex, int endIndex):截取指定范围(不包含 endIndex)。
  • replace(char oldChar, char newChar):替换所有指定字符。
  • trim():去除首尾空白字符。

示例

1
2
3
4
5
String str = "  Hello, Java  ";
System.out.println(str.substring(2)); // "Hello, Java "
System.out.println(str.substring(2, 7)); // "Hello"
System.out.println(str.replace('l', 'L')); // " HeLLo, Java "
System.out.println(str.trim()); // "Hello, Java"

分割与连接

  • split(String regex):按正则表达式分割字符串,返回字符串数组。
  • join(CharSequence delimiter, CharSequence... elements):(JDK 8+)用分隔符连接多个字符串。

示例

1
2
3
4
5
6
String str = "a,b,c";
String[] arr = str.split(",");
System.out.println(Arrays.toString(arr)); // [a, b, c]

String joined = String.join("-", "x", "y", "z");
System.out.println(joined); // x-y-z

格式化

  • format(String format, Object... args):格式化字符串。
  • valueOf(Object obj):将对象转换为字符串。

示例

1
2
3
4
5
String formatted = String.format("Name: %s, Age: %d", "Alice", 25);
System.out.println(formatted); // Name: Alice, Age: 25

String num = String.valueOf(123);
System.out.println(num); // "123"

性能与注意事项

字符串拼接

使用 + 拼接字符串在循环中会导致性能问题,因为每次拼接都会创建新对象。推荐使用 StringBuilderStringBuffer

1
2
3
4
5
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
sb.append(i);
}
String result = sb.toString();

== vs equals()

== 比较的是引用是否相同,而 equals() 比较内容是否相同:

1
2
3
4
String s1 = "hello";
String s2 = new String("hello");
System.out.println(s1 == s2); // false(不同对象)
System.out.println(s1.equals(s2)); // true(内容相同)

常量池与 intern()

调用 intern() 方法可以将字符串放入常量池,并返回常量池中的引用(谨慎使用,可能引发 Full GC):

1
2
3
String s1 = new String("hello").intern();
String s2 = "hello";
System.out.println(s1 == s2); // true(指向常量池同一对象)

后记

String 类是 Java 中最常用的类之一,其不可变性、常量池机制和丰富的 API 使其在字符串处理中非常强大。

评论