字符串操作是计算机程序设计中最常见的行为。
String
一般声明一个字符串类型,常用的是String类,String对象是不可变的。String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。
String中如何实现不可变设计?
查看String类的源码可以看到:
发现该类、类中的属性都是final的,在此回顾final修饰的意义:
1)属性用final修饰保证了属性时只读的,不能修改;
2)类用final修饰保证了该类中的方法不能被覆盖,该类不能被继承,防止子类无意间破坏不可变性
看个String不可变的例子 :
可以看到q的值没有变过,这是因为当q传给upcase()方法的时候,实际上传递是一个引用的拷贝。其实,每当把String对象作为方法的参数的时候,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未动过。
String中的“+”
在这个例子中,编译器创建了一个StringBuilder对象,用以构造最终的q,并为每个字符串调用一次StringBuilder中的append方法,最后调用toString()生成结果,并重新存为q。
保护性拷贝(String要考虑线程安全问题吗?)
有时候我们会注意到,使用字符串的时候,也有一些修改相关的方法,比如substring(),接下来看看这个方法的底层源码:
发现其内部是调用了String的构造方法创建了一个新的字符串,再进入这个构造方法看看,是否对final char value[]做出了修改:
并没有对final char value[]做修改,构造新的字符串对象时,会生成新的char[] value,对内容进行复制。这种通过创建副本的方法来避免共享的手段称之为保护性拷贝。这也是为什么String不需要考虑线程安全问题。
StringBuilder
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
查看StringBuilder底层代码:
发现调用的都是父类构造器,再到父类中看:
发现value并不是final类型。不存在不可变性。
StringBuffer
StringBuffer是可变的,并且是线程安全的,其线程安全是因为每个方法加了synchronized关键字。
StringBuffer与StringBuilder除了线程安全的区别没有太大区别,但是一般线程安全的话,效率会降低,所以StringBuilder的效率高于StringBuffer。