首先明确什么是不可变字符序列?
我们都知道在Java中, String类是不可变的。那么到底什么是不可变的对象呢? 可以这样理解:如果一个对象创建完成之后,不能再改变它的状态,那么该对象就是不可变的。不能改变状态的意思是,不能改变对象内的属性,包括基本数据类型的值不能改变,引用类型指向的对象的状态也不能改变。或者这样理解就是对象状态不可变,而引用是可再次指向其他对象的。
首先说明一点不是说String类被final修饰就是不可变的,而是成员变量(域)被final修饰
源码中是这样体现的
/** The value is used for character storage. */
private final char value[];
具体看如下代码运行结果(都是针对同一对象进行修改操作)
public class Demo01 {
public static void main(String[] args) {
String s1=new String("abcdef");
System.out.println(Integer.toHexString(s1.hashCode()));
System.out.println(s1);
s1.substring(3, 5);
System.out.println(Integer.toHexString(s1.hashCode()));
System.out.println(s1);
}
}
内存地址为ab199863
abcdef
内存地址为ab199863
abcdef
对同一对象进行修改操作没有发生改变即为不可变
为什么StringBuilder与StringBuffer是可变的?
源码中是这样体现的(摘自父类AbstractStringBuilder)
/**
* The value is used for character storage.
*/
char[] value;
public class Demo02 {
> public static void main(String[] args) {
> StringBuilder sb=new StringBuilder("abcdef");
>
> System.out.println(Integer.toHexString(sb.hashCode()));
> System.out.println(sb);
>
> sb.setCharAt(0, 'A');
>
> System.out.println(Integer.toHexString(sb.hashCode()));
> System.out.println(sb);
> }
> }
内存地址为15db9742
abcdef
内存地址为15db9742
Abcdef
对同一对象进行修改操作发生改变即为可变
接下来看三者间的效率
我们对5万个数进行字符串拼接效率如下
如图可得,三者间的效率差距有多大
StringBuilder与StringBuffer的区别
StringBuilder:线程不安全,效率高
StringBuffer:线程安全:效率低
摘自JDK1.8 StringBuffer的append部分重载方法源码
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
由于append方法加了同步拼接时导致效率低
return this可以进行链式调用
根据effective java第51条规定:
通过字符串连接符进行操作时,时间复杂度为O(n2),这是由于字符串不可变而导致的结果,当两个字符串被连接在一起时,他们的内容都要被拷贝,相反,而应该使用StringBuilder的append方法,时间是线性的,另一种方法是使用字符数组,或者每次只处理一个字符串
可变即可扩容
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
扩容实际上就是数组的拷贝