上一篇博客:Java String详解(二)
写在前面:大家好!我是
晴空๓
。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教。我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油,冲鸭!
用知识改变命运,用知识成就未来!加油 (ง •̀o•́)ง (ง •̀o•́)ง
文章目录
- 前言
- 什么时候可以使用"+"进行拼接?
- 常量折叠优化
- 总结
- intern()
前言
上一篇博客:Java String详解(二) 主要说明了一下 Java 字符串截取和拼接操作,本篇继续往下写。
什么时候可以使用"+"进行拼接?
上一篇博客说到 Java中使用 + 拼接字符串本质上是通过 StringBuilder 调用 append() 方法实现的,在循环中使用 + 会重复创建 StringBuilder 对象导致堆内存中对象数量迅速增加,内存开销比较大;对象在使用后很快就会变成垃圾,需要被垃圾回收器处理。这增加了垃圾回收的频率和压力,特别是在循环次数很多的情况下,可能会引起性能问题;
常量折叠优化
但是当拼接操作不涉及循环或者只涉及少量的字符串拼接时,使用 + 运算符是一个简单直观的选择。特别是在单行代码中拼接几个字符串字面量时,+ 运算符的可读性更好。而且对于简单的字符串拼接,编译器会进行一个叫做 常量折叠(Constant Folding) 的代码优化,常量折叠会把常量表达式的值求出来作为常量嵌在最终生成的代码中,即 String str = "he" + "llo" + " wor" + "ld";
编译器会优化成 String str = "hello world";
public class StringTest {public static void main(String[] args) {String str = "he" + "llo" + " wor" + "ld";System.out.println(str);}
}
使用 javac 反编译之后可以看到字节码如下:
Compiled from "StringTest.java"
public class StringTest {public StringTest();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: ldc #2 // String hello world2: astore_13: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;6: aload_17: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V10: return
}
通过 0: ldc #2 // String hello world
即可看出编译器 常量折叠 的代码优化。但是并不是所有的常量都会进行折叠,只有编译器在程序编译期就可以确定值的常量才可以。
总结
当我们需要在循环中或者有大量的字符串拼接操作时推荐使用 StringBuilder 进行拼接;当拼接操作不涉及循环只涉及少量字符串拼接时可以使用 + 运算符进行拼接;当在多线程环境下进行字符串拼接时,需要考虑到线程安全问题,由于 StringBuilder 不是线程安全的,推荐使用 StringBuffer 进行拼接。
intern()
在之前博客 Java String详解(一)中提到当我们直接使用字面量赋值创建 String 对象时,声明出的字符串对象会直接存储在字符串常量池中。如果不是用双引号声明的 String 对象,可以使用 String 提供的 intern 方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中,然后返回字符串常量池中的引用。
public class StringTest {public static void main(String[] args) {String str1 = new String("hello");String str2 = str1.intern();String str3 = "hello";System.out.println(str1 == str2); // falseSystem.out.println(str2 == str3); // true}
}
通过使用 intern() 方法可以节省内存,但是在一般情况下不要使用该方法
,频繁使用该方法可能会导致 OOM 问题。因为 intern() 所使用的是 JVM heap 中 PermGen 相应的区域,在 JVM 中该区域是用来存放装载类和创建类实例时用到的原数据,垃圾收集器不会对缓存的字符串做垃圾回收。1
未完待续。。。
《大话Java性能优化》 ↩︎