首先要知道为什么会有StringBuffer 和 StringBuilder这两个东西?要知道这个就要去研究一下String,简单来说String。
首先要知道String是被final修饰的,因此它是一个常量,是要保存在常量池中的,这么说对,也不对,因为String有两种保存方式,一种是使用类似String a = "123",这样的形式,如果检查常量池中没有相同的常量,则会在常量池中创建"123"个常量,而将这个常量的引用给到a和b,所以a和b指向的都是一个常量,当然a==b的结果会是true。而另一种类似String a = new String("123");这样的形式就被当作对象创建在堆中,而没有常量池什么事情了,每一次的String创建都相当于创建了一个新的对象,而不同对象用==可以想象会是false。
这个其实是String做的一个优化,那么有新的问题来了,常量池中的"123"加上堆中创建对象的"123"会是什么效果呢?
这个结果会是false,要理解这个结果可以使用javap -c Test.class 来查看下编译后的结果:
可以发现JVM为我们的String做了优化,将原本的String+String形式变成了StringBuilder.append这个形式。其实JVM这么做的原因是不想让我们因为仅仅是使用一个String+String 就创建一个新对象,特别是频繁的修改String的时候,会造成大量内存的占用。而使用StringBuilder.appen这种形式能够使得只创建一个对象,大大减少了内存的浪费。但是也是因为创建了StringBuilder对象使得上面的等式无法成立。
JVM在此选择了效率更高的StringBuilder做优化。因为StringBuffer每个方法都通过synchronized的排它锁使得它是线程安全的,但同样意味着效率的降低。
由此分析,一般来说在多线程的情况下,有可能会出现并发问题的时候,选用已经实现了synchronized排它锁的StringBuffer能够更安全。而在单线程更追求效率的场景下,更适合选用StringBuilder进行字符串的操作。而String很不适合在反复需要修改时使用,因为,不是所有情况虚拟机都会帮忙优化成为StringBuilder的,在某些情况下虚拟机并不能完成优化,这就会使得修改String会不停的创建出没有意义的对象,占用内存。所以当有需要拼接String的场景尽量使用StringBuilder和StringBuffer。