字符串操作
一、Java 中操作字符串都有哪些类?它们之间有什么区别?
String、StringBuffer、StringBuilder
String : final修饰,是不可变的,所以线程安全,String类的方法都是返回new String。即对String对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象
//String类是final修饰,底层是用char数组来存储,也是用final修饰,不可变的类都是线程安全的
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
//存放数组
private final char value[];
}
-
StringBuffer:线程安全的,对字符串的操作的方法都加了synchronized,保证线程安全
-
StringBuilder :线程不安全
//StringBuffer与StringBuilder都继承AbstractStringBuilder,底层也是用char数组存储
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
//默认数组长度16
public StringBuilder() {
super(16);
}
//如果调用有参构造,长度则为16 + 字符串长度
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
//StringBuilder与StringBuffer在appen时都调用父类的append方法
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
public StringBuffer() {
super(16);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
//StringBuffer类中,所有的方法都加了synchronized,所以线程安全
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
}
//StringBuffer与StringBuilder的父类
abstract class AbstractStringBuilder implements Appendable, CharSequence {
//用char数组来存放数据,没有用final修饰,所以可变
char[] value;
//字符串具体的长度, char数组的长度!=字符串长度
int count;
//char数组的长度调用capacity方法
public int capacity() {
return value.length;
}
//每次调用append时,都产生一个新的char数组,并两次进行数组copy
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
//数组扩容,每次复制一个新的数组
ensureCapacityInternal(count + len);
//将要添加的字符串复制到新数组中
str.getChars(0, len, value, count);
count += len;
return this;
}
}
二、StringBuffer与StringBuilder底层扩容机制
abstract class AbstractStringBuilder implements Appendable, CharSequence {
//底层扩容,调用数组copyOf方法
private void ensureCapacityInternal(int minimumCapacity) {
//如果append时,最小长度大于当前数组的长度,出发扩容
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
private int newCapacity(int minCapacity) {
//扩容后新数组的长度 = 老数组的长度2倍 + 2
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
//不超过int最大范围
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
}
三、string类为什么是final的?
主要是为了“效率” 和 “安全性” 的缘故。
1、 由于String类不能被继承,所以就不会没修改,这就避免了因为继承引起的安全隐患
2、若 String允许被继承, 由于它的高度被使用率, 可能会降低程序的性能,所以String被定义成final。
String存放数据的char数组value用final修饰,表示该数组引用地址不可变,数组中内容可以通过反射来修改