网站首页 > java教程 正文
Java 中的字符串拼接是一个非常常见的操作。表面上看,使用 + 运算符拼接字符串似乎很简单,但实际上其背后隐藏了许多复杂的实现细节和优化机制。
字符串拼接的基本示例
让我们从一个简单的示例开始:
java
public class StringConcatExample {
public static void main(String[] args) {
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";
String str4 = str1 + str2;
String str5 = "string";
System.out.println(str3 == str4); // false
System.out.println(str3 == str5); // true
System.out.println(str4 == str5); // false
}
}
在这个示例中,我们使用了不同的方式拼接字符串,并通过 == 运算符比较它们的引用是否相同。接下来,我们将深入分析这些拼接操作的底层实现。
编译期的常量折叠
对于编译期可以确定值的字符串,即常量字符串,JVM 会将其存入字符串常量池。而字符串常量拼接得到的字符串常量在编译阶段就已经被存放到字符串常量池。这归功于编译器的优化——常量折叠(Constant Folding)。
常量折叠是一种编译期优化技术,它会将常量表达式的值直接计算出来,并替换为具体的常量。在 Java 编译器中,常量折叠是对源代码进行的少数优化之一。
让我们通过一个具体的例子来理解这一点:
String str3 = "str" + "ing";
上述代码在编译阶段会被优化成:
String str3 = "string";
这意味着 str3 直接指向常量池中的字符串 "string"。我们可以通过反编译工具(如 javap)查看编译后的字节码,验证这一点。
javap -c StringConcatExample
反编译结果显示,str3 直接被初始化为常量 "string"。
引用类型的字符串拼接
对于引用类型的字符串拼接,编译器无法在编译期确定其值,此时会使用 StringBuilder 进行拼接。
String str4 = str1 + str2;
上述代码在编译后实际变成了:
java
String str4 = new StringBuilder().append(str1).append(str2).toString();
因此,str4 是在堆上新创建的一个对象,而不是常量池中的对象。这也是为什么 str3 == str4 会返回 false。
使用 final 修饰的字符串变量
如果字符串变量被 final 修饰,编译器会将其视为常量进行处理,从而在编译期确定其值。
java
final String str1 = "str";
final String str2 = "ing";
String c = "str" + "ing"; // 常量池中的对象
String d = str1 + str2; // 常量池中的对象
System.out.println(c == d); // true
被 final 修饰的字符串变量会被编译器当作常量处理,其拼接结果在编译期就确定了,因此 c 和 d 指向同一个常量池中的对象。
运行时确定值的字符串拼接
如果字符串的值在运行时才能确定,那么编译器无法对其进行优化,此时会在堆上创建新的对象。
java
final String str1 = "str";
final String str2 = getStr();
String c = "str" + "ing"; // 常量池中的对象
String d = str1 + str2; // 在堆上创建的新的对象
System.out.println(c == d); // false
在上述代码中,由于 str2 的值在运行时才能确定,因此 d 是在堆上新创建的对象。
java
public static String getStr() {
return "ing";
}
性能对比
为了避免频繁创建字符串对象,建议在需要频繁拼接字符串时使用 StringBuilder 或 StringBuffer。以下是一个简单的性能对比示例:
java
public class StringPerformanceTest {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
String result = "";
for (int i = 0; i < 10000; i++) {
result += "test";
}
long endTime = System.currentTimeMillis();
System.out.println("String: " + (endTime - startTime) + " ms");
startTime = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append("test");
}
result = sb.toString();
endTime = System.currentTimeMillis();
System.out.println("StringBuilder: " + (endTime - startTime) + " ms");
}
}
运行结果可能如下:
txt
String: 500 ms
StringBuilder: 5 ms
可以看出,使用 StringBuilder 拼接字符串的性能远优于直接使用 + 运算符。这是因为每次使用 + 运算符拼接字符串时,都会创建一个新的 StringBuilder 对象,然后将其转换为字符串,造成大量不必要的对象创建和销毁。
深入源码解析
为了更详细地理解字符串拼接的底层实现,我们可以深入查看 StringBuilder 和 String 类的源码。
StringBuilder 的 append 方法
java
public StringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
StringBuilder 的 append 方法会确保内部字符数组有足够的容量,然后将待拼接的字符串复制到字符数组中。
String 的 toString 方法
java
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
StringBuilder 的 toString 方法会创建一个新的 String 对象,并将内部字符数组的内容复制到新的 String 对象中。
总结
Java 中字符串拼接的实现机制。主要内容包括:
- 编译期的常量折叠优化。
- 引用类型字符串拼接的底层实现。
- 使用 final 修饰字符串变量的优化。
- 运行时确定值的字符串拼接。
- 性能对比及优化建议。
- 深入源码解析 StringBuilder 和 String 的实现细节。
?
猜你喜欢
- 2024-10-18 java面试题之三:字符串操作(字符串笔试题java)
- 2024-10-18 Jmeter BeanShell循环:字符串 拼接模式,为变量名 获取数据值
- 2024-10-18 连接字符串除了“+”还有哪些方法,进来看
- 2024-10-18 初识java—(三十四)String、StringBuffer和StringBuilder类
- 2024-10-18 初学者都在坑里!不要在Python中使用“+”来连接字符串
- 2024-10-18 一段简单的关于字符串的Java 代码,竟然考察了这么多东西
- 2024-10-18 Java中字符串连接运算符“+”的困惑
- 2024-10-18 Guava之字符串连接,分割,匹配处理
- 2024-10-18 【老梁聊IT之JAVA篇】StringBuilder的正确使用方法详解
- 2024-10-18 为何Java8中不再需要StringBuilder拼接字符串
你 发表评论:
欢迎- 07-15采用Oracle OSB总线进行服务注册和接入
- 07-15javaEE 新闻管理系统 oracle11+tomcat6
- 07-15从Oracle演进看数据库技术的发展(oracle数据库发展史)
- 07-15如何升级oracle数据库安全补丁(oraclepsu补丁升级)
- 07-15【权威发布】关于Oracle WebLogic Server未授权远程代码执行高危漏洞的预警通报
- 07-15【mykit-data】 数据库同步工具(数据库表同步工具)
- 07-15[Java速成] 数据库基础,Connector/J、JDBC、JPA的关系(day 7)
- 07-15Google前工程主管“入住”Oracle(google浏览器找不到以前的书签)
- 最近发表
-
- 采用Oracle OSB总线进行服务注册和接入
- javaEE 新闻管理系统 oracle11+tomcat6
- 从Oracle演进看数据库技术的发展(oracle数据库发展史)
- 如何升级oracle数据库安全补丁(oraclepsu补丁升级)
- 【权威发布】关于Oracle WebLogic Server未授权远程代码执行高危漏洞的预警通报
- 【mykit-data】 数据库同步工具(数据库表同步工具)
- [Java速成] 数据库基础,Connector/J、JDBC、JPA的关系(day 7)
- Google前工程主管“入住”Oracle(google浏览器找不到以前的书签)
- Oracle数据库云服务系列新增前所未有的企业级功能
- 直播预告丨如何实现Oracle存储过程到java的一键转化
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)