深入解析String、StringBuffer、StringBuilder

📅 2026/7/28 15:11:50
深入解析String、StringBuffer、StringBuilder
String是Java中基础且重要的类并且String也是Immutable类的典型实现被声明为final class除了hash这个属性其它属性都声明为final,因为它的不可变性所以例如拼接字符串时候会产生很多无用的中间对象如果频繁的进行这样的操作对性能有所影响。StringBuffer就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类提供append和add方法可以将字符串添加到已有序列的末尾或指定位置它的本质是一个线程安全的可修改的字符序列把所有修改数据的方法都加上了synchronized。但是保证了线程安全是需要性能的代价的。在很多情况下我们的字符串拼接操作不需要线程安全这时候StringBuilder登场了StringBuilder是JDK1.5发布的它和StringBuffer本质上没什么区别就是去掉了保证线程安全的那部分减少了开销。StringBuffer 和 StringBuilder 二者都继承了 AbstractStringBuilder 底层都是利用可修改的char数组(JDK 9 以后是 byte数组)。所以如果我们有大量的字符串拼接如果能预知大小的话最好在new StringBuffer 或者StringBuilder 的时候设置好capacity避免多次扩容的开销。扩容要抛弃原有数组还要进行数组拷贝创建新的数组。我们平日开发通常情况下少量的字符串拼接其实没太必要担心例如String str “aa”“bb”“cc”;像这种没有变量的字符串编译阶段就直接合成aabbcc了然后看字符串常量池下面会说到常量池里有没有有也直接引用没有就在常量池中生成返回引用。如果是带变量的其实影响也不大JVM会帮我们优化了。(以下运行JDK版本为1.8)看看反编译结果String拼接用的是StringBuilder.append。是不是好像觉得那不平时不需要用StringBuilder 啊用String就好了啊都帮我们优化了不是的来看这个情况看看反编译结果new了很多次StringBuilder也就是说会如果有大量的字符串拼接new好多StringBuilder对象所以频繁的字符串操作还是得用StringBuilder再说说字符串常量池看看我们的代码你会发现String是真的频繁得使用到Java为了避免在一个系统中产生大量的String对象引入了字符串常量池。创建一个字符串时首先会检查池中是否有值相同的字符串对象如果有就直接返回引用不会创建字符串对象如果没有则新建字符串对象返回对象引用并且将新创建的对象放入池中。但是通过new方法创建的String对象是不检查字符串常量池的而是直接在堆中创建新对象也不会把对象放入池中。上述原则只适用于直接给String对象引用赋值的情况。String str1 new String(“a”); //不检查字符串常量池的String str2 “bb”; //检查字符串常量池的String还提供了intern()方法。调用该方法时如果字符串常量池中包括了一个等于此String对象的字符串由equals方法确定则返回池中的字符串的引用。否则将此String对象添加到池中并且返回此池中对象的引用。在JDK6中不推荐大量使用intern方法因为这个版本字符串缓存在永久代中这个空间是有限了除了FullGC之外不会被清楚所以大量的缓存在这容易OutOfMemoryError。之后的版本把字符串放入了堆中避免了永久代被挤满。总结1、在字符串不经常发生变化的业务场景优先使用String(代码更清晰简洁)。如常量的声明少量的字符串操作(拼接删除等)。2、在单线程情况下如有大量的字符串操作情况应该使用StringBuilder来操作字符串。不能使用String来拼接而是使用避免产生大量无用的中间对象耗费空间且执行效率低下新建对象、回收对象花费大量时间。如JSON的封装等。3、在多线程情况下如有大量的字符串操作情况应该使用StringBuffer。如HTTP参数解析和封装等。