You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中使用+或concat()拼接字符串时,新String对象的创建位置疑问

Java字符串拼接(+/concat)的对象创建逻辑详解

嘿,这个问题真的戳中了Java字符串的核心知识点——常量池和堆的区别,很多刚入门的小伙伴都会在这里卡壳,我来给你一步步讲明白~

先明确你的代码里各变量的情况

先回顾下你给出的代码:

String str1 = "Hello"; 
String str2 = "World"; 
String str3 = new String("HelloWorld"); 
String str4 = str1 + str2; 

你已经知道:

  • str1、str2指向字符串常量池里的"Hello"、"World"对象
  • str3在**堆内存(常量池外)**创建新对象,同时常量池里会生成"HelloWorld"(因为字面量"HelloWorld"触发了常量池存储)

关于+拼接的执行逻辑

这里要分两种核心情况:

1. 拼接的是编译期常量

如果是直接用字面量或者final修饰的常量拼接,比如:

final String str1 = "Hello";
final String str2 = "World";
String str4 = str1 + str2;
String str5 = "HelloWorld";
System.out.println(str4 == str5); // 输出true

这时候编译器会在编译阶段直接把str1 + str2优化成"HelloWorld",所以str4直接指向常量池里的对象,和str5的引用完全相同。

2. 拼接的是非编译期常量(普通变量)

这就是你代码里的情况:str1和str2是普通变量,编译器无法在编译期确定它们的最终值。此时Java会把str1 + str2转换成如下逻辑:

String str4 = new StringBuilder().append(str1).append(str2).toString();

而StringBuilder.toString()的底层实现是直接调用new String(value, 0, count)创建新对象,这个对象是在**堆内存(常量池之外)**的,不会自动进入字符串常量池。所以str4和常量池里的"HelloWorld"引用完全不同。

关于concat()方法的执行逻辑

str1.concat(str2)的底层逻辑也很清晰:

  1. 先计算拼接后的总长度,如果总长度为0,直接返回原字符串
  2. 如果长度大于0,会创建一个新的char数组,把两个字符串的内容复制进去
  3. 最后通过new String(result, 0, totalLength)创建新对象,同样是在堆内存中,不会自动入池。

为什么不用new却在池外创建对象?

这个问题的核心是:变量拼接/concat的结果是运行时动态生成的,Java不会自动把这种运行时生成的字符串放进常量池(只有编译期确定的字面量才会自动入池)。

虽然你没有手动写new,但拼接操作的底层API(比如StringBuilder.toString()、String.concat())已经帮你调用了new String(),只是这个过程被封装起来了,不需要你手动编写而已。

如果想让运行时生成的字符串进入常量池,可以手动调用intern()方法:

String str4 = (str1 + str2).intern();
String str5 = "HelloWorld";
System.out.println(str4 == str5); // 输出true

intern()方法会检查常量池里是否有相同内容的字符串,如果有就返回池里的引用;如果没有,就把当前字符串的内容放进常量池并返回引用。

内容的提问来源于stack exchange,提问作者404 Brain Not Found

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:55:54