Java的String类作为日常开发中使用频率最高的类之一,提供了多个重载的构造方法,不同构造方法在内存分配、数据复制等层面的处理逻辑存在差异,这些差异会直接影响程序的性能表现。

String常见构造方法分类
Java中String的常用构造方法主要分为以下几类,不同构造方法的参数类型和实现逻辑各不相同:
- 无参构造方法:String()
- 接收字符串参数的构造方法:String(String original)
- 接收字符数组参数的构造方法:String(char[] value)、String(char[] value, int offset, int count)
- 接收字节数组参数的构造方法:String(byte[] bytes)、String(byte[] bytes, int offset, int length, String charsetName)等
- 接收StringBuffer或StringBuilder参数的构造方法:String(StringBuffer buffer)、String(StringBuilder builder)
各构造方法的实现逻辑与性能分析
1. 无参构造方法String()
无参构造方法的实现逻辑非常简单,会创建一个空字符串对象,内部字符数组指向一个预定义的空字符数组常量。由于不需要额外的数据复制操作,该构造方法的性能开销极低,适合需要创建空字符串对象的场景。
// 无参构造方法源码简化逻辑
public String() {
this.value = "".value; // 指向空字符串的字符数组常量
}
2. 接收String参数的构造方法String(String original)
该构造方法会创建一个新的String对象,但是不会复制original的字符数组,而是直接共享original的value数组,同时复制hash值。这种实现避免了不必要的数组复制,性能表现较好,适合需要基于已有字符串创建新字符串对象的场景。
// 接收String参数的构造方法源码简化逻辑
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
3. 接收字符数组的构造方法
String(char[] value)和String(char[] value, int offset, int count)构造方法会复制传入的字符数组内容到新的字符数组中,避免外部修改字符数组影响String对象的不可变性。由于涉及数组复制操作,当字符数组长度较大时,性能开销会明显高于前两种构造方法。
// 接收完整字符数组的构造方法源码简化逻辑
public String(char[] value) {
this.value = Arrays.copyOf(value, value.length);
}
// 接收部分字符数组的构造方法源码简化逻辑
public String(char[] value, int offset, int count) {
// 边界校验逻辑省略
this.value = Arrays.copyOfRange(value, offset, offset + count);
}
4. 接收字节数组的构造方法
这类构造方法除了需要复制字节数组内容外,还需要进行字符编码解码操作,将字节数组转换为字符数组,因此性能开销是所有构造方法中最大的。尤其是需要指定字符集的场景,编码转换的过程会进一步增加性能消耗。
// 接收字节数组和字符集的构造方法源码简化逻辑
public String(byte[] bytes, int offset, int length, String charsetName) {
// 边界校验逻辑省略
if (charsetName == null) {
throw new NullPointerException("charsetName");
}
// 编码转换逻辑,涉及额外的计算开销
StringCoding.Result ret = StringCoding.decode(charsetName, bytes, offset, length);
this.value = ret.value;
}
5. 接收StringBuffer和StringBuilder的构造方法
String(StringBuffer buffer)构造方法会先对buffer加锁,再复制其内部的字符数组,由于加锁操作和数组复制的双重开销,性能表现一般。而String(StringBuilder builder)构造方法不需要加锁,仅进行数组复制,性能略优于接收StringBuffer的构造方法。
// 接收StringBuffer的构造方法源码简化逻辑
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
// 接收StringBuilder的构造方法源码简化逻辑
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
性能对比总结
通过各构造方法的实现逻辑分析,可以得出不同构造方法的性能排序(从优到劣):
| 构造方法类型 | 性能表现 | 适用场景 |
|---|---|---|
| 无参构造方法 | 最优 | 创建空字符串对象 |
| 接收String参数的构造方法 | 次优 | 基于已有字符串创建新对象 |
| 接收字符数组的构造方法 | 中等 | 需要基于字符数组创建字符串,且字符数组不会被外部修改 |
| 接收StringBuilder的构造方法 | 中等偏下 | 基于StringBuilder构建的字符串创建String对象 |
| 接收StringBuffer的构造方法 | 较差 | 基于StringBuffer构建的字符串创建String对象 |
| 接收字节数组的构造方法 | 最差 | 需要将字节数组转换为字符串,必须使用时注意字符集选择 |
开发中的使用建议
在实际开发中,可以根据场景选择合适的构造方法:
- 如果需要创建空字符串,直接使用无参构造方法或者字符串字面量赋值,避免不必要的开销。
- 基于已有字符串创建新对象时,优先使用String(String original)构造方法,或者直接使用字符串赋值,JVM会进行常量池优化。
- 当字符数组已经确定不会被修改时,可以考虑使用String(char[])构造方法,如果字符数组后续可能被修改,该构造方法可以避免外部修改影响String对象的值。
- 尽量避免使用字节数组构造方法,如果必须使用,提前确定好字符集,减少编码转换的开销。
- 在单线程场景下拼接字符串构建String对象时,优先使用StringBuilder再调用String(StringBuilder)构造方法,多线程场景下使用StringBuffer再调用对应构造方法。
需要注意的是,Java 9之后String的内部存储从char数组改为byte数组,配合编码标记来节省内存,但不同构造方法的核心性能差异逻辑和上述分析保持一致,开发者可以根据实际使用的JDK版本参考对应源码进行验证。