杰瑞科技汇

Java字符串加号底层是new String吗?

在 Java 中,主要有三种方式来实现字符串连接:

Java字符串加号底层是new String吗?-图1
(图片来源网络,侵删)
  1. 使用 运算符(最常用、最直观)
  2. 使用 String.concat() 方法
  3. 使用 StringBuilderStringBuffer(性能最高,尤其在循环中)

下面我们逐一介绍这三种方法,并分析它们的优缺点和适用场景。


使用 运算符

这是最常见、最符合直觉的方法,尤其是在拼接少量字符串时。

示例代码

public class StringConcatExample {
    public static void main(String[] args) {
        String str1 = "Hello, ";
        String str2 = "World!";
        // 使用 + 运算符连接字符串
        String result = str1 + str2;
        System.out.println(result); // 输出: Hello, World!
        // 也可以连接字符串和其他类型(如数字)
        int number = 100;
        String info = "The number is: " + number;
        System.out.println(info); // 输出: The number is: 100
    }
}

工作原理(非常重要!)

当你使用 连接字符串时,Java 编译器会在底层悄悄地为你创建一个 StringBuilder 对象,然后调用其 append() 方法,最后再调用 toString() 方法返回结果。

以下代码:

Java字符串加号底层是new String吗?-图2
(图片来源网络,侵删)
String a = "a";
String b = "b";
String c = "c";
String result = a + b + c;

在编译后,其效果类似于:

String a = "a";
String b = "b";
String c = "c";
// 编译器自动创建 StringBuilder
StringBuilder sb = new StringBuilder();
sb.append(a);
sb.append(b);
sb.append(c);
String result = sb.toString();

优缺点

  • 优点

    • 语法简洁:代码可读性高,非常直观。
    • 功能强大:可以自动将任何数据类型(如 int, double, Object 等)转换为字符串再进行连接。
  • 缺点

    • 性能问题(在循环中):如果在循环里使用 ,会频繁地创建和销毁 StringBuilder 对象,导致性能急剧下降。
    • 不可变性String 对象是不可变的,每次使用 连接,都会在内存中创建一个新的 String 对象,旧的 String 对象会被垃圾回收器回收,这会带来额外的内存开销。

适用场景

  • 适用于少量、非循环的字符串拼接。
  • 在日志打印、简单的字符串组合等场景下非常方便。

使用 String.concat() 方法

String 类本身提供了一个 concat() 方法用于连接另一个字符串。

Java字符串加号底层是new String吗?-图3
(图片来源网络,侵删)

示例代码

public class StringConcatMethodExample {
    public static void main(String[] args) {
        String str1 = "Hello, ";
        String str2 = "World!";
        // 使用 concat() 方法连接字符串
        String result = str1.concat(str2);
        System.out.println(result); // 输出: Hello, World!
    }
}

工作原理

concat() 方法直接在调用它的字符串对象后面追加传入的字符串,并返回一个新的 String 对象,它内部实现也依赖于 System.arraycopy 等数组拷贝方法来高效地创建新字符串。

优缺点

  • 优点

    • 语义明确:代码清晰地表达了“连接”这个意图,比 更具描述性。
    • 性能通常比 (在非循环中)稍好,因为它避免了 StringBuilder 的创建开销。
  • 缺点

    • 语法稍显冗长:没有 运算符简洁。
    • 功能较弱concat() 的参数必须是 String 类型,如果需要连接数字等其他类型,必须先调用 String.valueOf()Integer.toString() 等方法进行转换,否则会编译错误。
    • 同样,在循环中使用性能会很差。

适用场景

  • 当你想明确表达连接操作,并且确定所有要连接的元素都是 String 类型时。
  • 适用于少量字符串的拼接。

使用 StringBuilderStringBuffer

这是性能最高的字符串连接方式,特别是在需要拼接大量字符串或循环中。StringBuilderStringBuffer 的用法几乎完全相同,核心区别在于线程安全

  • StringBuilder非线程安全,但性能更高(因为没有同步开销)。绝大多数情况下,你应该优先使用 StringBuilder
  • StringBuffer线程安全,方法上有关键字 synchronized,在多线程环境下修改字符串内容时使用,但日常开发中很少遇到。

示例代码

public class StringBuilderExample {
    public static void main(String[] args) {
        String str1 = "Hello, ";
        String str2 = "World!";
        // 创建一个 StringBuilder 对象
        StringBuilder sb = new StringBuilder();
        // 使用 append() 方法追加字符串
        sb.append(str1);
        sb.append(str2);
        // 使用 toString() 方法获取最终的 String 结果
        String result = sb.toString();
        System.out.println(result); // 输出: Hello, World!
        // 也可以链式调用
        String result2 = new StringBuilder().append("a").append("b").append("c").toString();
        System.out.println(result2); // 输出: abc
    }
}

工作原理

StringBuilder 内部维护一个可变的字符数组,当你调用 append() 方法时,它会将内容添加到这个数组中(如果数组不够大,会进行扩容),而不会创建新的对象,只有在最后调用 toString() 时,才会生成一个新的、不可变的 String 对象。

优缺点

  • 优点

    • 性能卓越:在循环中或拼接大量字符串时,性能远超 和 concat(),因为它只在内存中维护一个可变对象,避免了频繁创建新对象的开销。
    • 功能强大append() 方法可以接受各种数据类型,就像 运算符一样。
  • 缺点

    • 代码稍显繁琐:需要手动创建对象、调用方法,最后还要 toString(),没有 运算符那么简洁。

适用场景

  • 必须用于循环中拼接字符串
  • 需要拼接非常多的字符串,对性能有要求的场景。
  • 在构建复杂的 SQL 语句、JSON 数据或 XML 数据时非常常用。

性能对比示例

下面是一个在循环中使用 和 StringBuilder 的性能对比,可以非常直观地看出差异。

public class PerformanceComparison {
    public static void main(String[] args) {
        int iterations = 100000;
        // --- 使用 + 运算符 ---
        long startTimePlus = System.nanoTime();
        String resultPlus = "";
        for (int i = 0; i < iterations; i++) {
            resultPlus += "a"; // 每次循环都创建一个新的 String 对象
        }
        long endTimePlus = System.nanoTime();
        System.out.println("Using + operator took: " + (endTimePlus - startTimePlus) / 1_000_000.0 + " ms");
        // --- 使用 StringBuilder ---
        long startTimeSB = System.nanoTime();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < iterations; i++) {
            sb.append("a"); // 在同一个对象上进行操作
        }
        String resultSB = sb.toString();
        long endTimeSB = System.nanoTime();
        System.out.println("Using StringBuilder took: " + (endTimeSB - startTimeSB) / 1_000_000.0 + " ms");
    }
}

可能的输出结果(具体时间因机器而异):

Using + operator took: 125.456 ms
Using StringBuilder took: 2.891 ms

从结果可以看出,在循环中,StringBuilder 的性能比 运算符快了数十倍甚至更多。


总结与最佳实践

方法 优点 缺点 适用场景
运算符 语法简洁,直观,可连接任意类型 循环中性能极差,频繁创建对象 少量、非循环的字符串拼接,如日志、简单赋值
concat() 语义明确,性能比 (非循环) 稍好 语法稍长,只能连接 String 类型 少量 String 对象的连接,强调连接意图
**StringBuilder
分享:
扫描分享到社交APP
上一篇
下一篇