三者按照字符串长度是否可变分为 可变字符串和不可变字符串
可变字符串:StringBuffer、StringBuilder
不可变字符串:String
它们底层都是数组结构,不同的是String底层定义的数组被final关键字修饰,而可变字符串没有。我们都知道被final修饰的引用变量不能指向其他的对象。这也就是String字符串长度不可变的根本原因。而可变对象就是通过把引用的重新指向,才能保证修改对象后不产生新的字符串对象。
StringBuffer和StringBuilder的区别?
StringBuffer是线程安全的,它的每一个方法都被synchronized关键字修饰。
StringBuilder不是线程安全的。由于这个原因效率会高于StringBuffer。不要求线程安全的情况下建议使用StringBuilder
在下文我主要对StringBuffer和String的区别进行讲解。
可变字符串的由来?
当我们需要完成字符串的大量拼接工作时,就是我们就面对着一个很头疼的问题,就是大
量的拼接字符串,导致内存空间的大量消耗。为什么这么说呢?String对象是不可变的。
到我们进行一次次的拼接操作时,实际上是创建了一个个全新的String对象。新对象包含着
我们修改后的字符串内容。而最初的String对象丝毫未动。完成大量的拼接操作后,我想我
们的jvm内存被字符串”吃“的不剩多少了吧。所以就引出了StringBuffer和StringBuilder。它
是的长度是可变的。我们对它的修改是在原字符串上进行的。当我们在对原字符串进行修改
后,这个修改后的字符串指向了原来的StringBuffer对象的引用。原来的老的对象就遗弃
了,等待被当作垃圾清理。这样就不会占用大量的内存空间了。请参考下面代码:
public class Demo1 {
@Test
public void stringTest01() {
StringBuffer buffer = new StringBuffer("String");
StringBuffer buffer1 = buffer.append("Buffer");
System.out.println(buffer == buffer1); //返回结果 true
}
@Test
public void stringTest02() {
String s1 = "String";
String s2 = s1+"!";
System.out.println(s1 == s2); //返回结果 false
}
}
其次StringBuffer和String相比,StringBuffer不仅仅有字符串长度(通过方法length()获取其
字符长度),同时也有容量(通过capacity()获取其容量)。通过空的构造方法创建的对象的
默认容量是16,它具有自动扩容的机制,随着字符串长度的增加,它会按照原字符串长度的
2倍加2的增长幅度进行增长。但是一般为了有更好的性能,我们在创建StringBuffer对象
时,预估一下大概字符串的长度,使用指定容量大小的方法进行构造,该做法主要是减少扩
容次数,提高执行效率。进行减少扩容的次数是优化可变字符串的一种方式和思路。