String
String 是被 final 修饰的类,不能被继承;String实现了 Serializable 和Comparable接口,表示String支持序列化和可以比较大小;String底层是通过char类型的数据实现的,并且被final修饰,所以字符串的值创建之后就不可以被修改,具有不可变性。
String 类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
String 实例化方式
- 通过字面量方式
String string = "张三";
2.通过new+构造器方式
String string = new String("张三");
两种方式的区别:
通过字面量方式为字符串赋值时,此时的字符串存储在方法区的字符串常量池中;
通过new+构造器方式实例化字符串时,字符串对象存储在堆中,但是字符串的值仍然存储在方法区的常量池中。
String字符串具有不可变性,当字符串重新赋值时,不管是对字符串进行拼接,还是调用String的replace()方法修改指定的字符或字符串,都不会在原来的内存地址进行修改,而是重新分配新的内存地址进行赋值。
StringBuffer
StringBuffer对象代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列,但都不会产生新的对象。通过StringBuffer生成的字符串,可以调用toString()方法将其转换为一个String对象。
StringBuffer b = new StringBuffer("abc");
b.append("123");
System.out.println(b);
b打印结果为:abc123
扩容
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
@HotSpotIntrinsicCandidate
public StringBuffer() {
super(16);
}
/**
* Constructs a string buffer with no characters in it and
* the specified initial capacity.
*
* @param capacity the initial capacity.
* @throws NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}.
*/
@HotSpotIntrinsicCandidate
public StringBuffer(int capacity) {
super(capacity);
}
/**
* Constructs a string buffer initialized to the contents of the
* specified string. The initial capacity of the string buffer is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
@HotSpotIntrinsicCandidate
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
StringBuffer()的初始容量可以容纳16个字符,当该对象的实体存放的字符的长度大于16时,实体容量就自动增加。StringBuffer对象可以通过length()方法获取实体中存放的字符序列长度,通过 capacity()方法来获取当前实体的实际容量。
StringBuffer(int capacity)可以指定分配给该对象的实体的初始容量参数为参数size指定的字符个数。当该对象的实体存放的字符序列的长度大于capacity个字符时,实体的容量就自动的增加。以便存放所增加的字符。
StringBuffer(String str)可以指定给对象的实体的初始容量为参数字符串s的长度额外再加16个字符。当该对象的实体存放的字符序列长度大于size个字符时,实体的容量自动的增加,以便存放所增加的字符。
扩容原理
StringBuffer 类
@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
StringBuffer的父类 AbstractStringBuilder
/**
* Appends the specified string to this character sequence.
* <p>
* The characters of the {@code String} argument are appended, in
* order, increasing the length of this sequence by the length of the
* argument. If {@code str} is {@code null}, then the four
* characters {@code "null"} are appended.
* <p>
* Let <i>n</i> be the length of this character sequence just prior to
* execution of the {@code append} method. Then the character at
* index <i>k</i> in the new character sequence is equal to the character
* at index <i>k</i> in the old character sequence, if <i>k</i> is less
* than <i>n</i>; otherwise, it is equal to the character at index
* <i>k-n</i> in the argument {@code str}.
*
* @param str a string.
* @return a reference to this object.
*/
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
putStringAt(count, str);
count += len;
return this;
}
/**
* For positive values of {@code minimumCapacity}, this method
* behaves like {@code ensureCapacity}, however it is never
* synchronized.
* If {@code minimumCapacity} is non positive due to numeric
* overflow, this method throws {@code OutOfMemoryError}.
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
int oldCapacity = value.length >> coder;
if (minimumCapacity - oldCapacity > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity) << coder);
}
}
/**
* Returns a capacity at least as large as the given minimum capacity.
* Returns the current capacity increased by the same amount + 2 if
* that suffices.
* Will not return a capacity greater than
* {@code (MAX_ARRAY_SIZE >> coder)} unless the given minimum capacity
* is greater than that.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero or
* greater than (Integer.MAX_VALUE >> coder)
*/
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = value.length >> coder;
int newCapacity = (oldCapacity << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
int SAFE_BOUND = MAX_ARRAY_SIZE >> coder;
return (newCapacity <= 0 || SAFE_BOUND - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
使用append()方法在字符串后面追加值的时候,如果长度超过了该字符串存储空间大小了就就会先进性扩容。构建新的并且存储空间更大的字符串,将旧的复制过去。
在进行字符串append添加的时候,会先计算添加后字符串大小,传入一个方法:ensureCapacityInternal 这个方法进行是否扩容的判断,需要扩容就调用expandCapacity方法进行扩容。
扩容规则
看newCapacity(int minCapacity) 方法
先 原始容量 * 2 + 2(加2是因为拼接字符串通常末尾都会有个多余的字符)
如果扩容了之后,容量够用,即新的容量就为扩容之后的容量。
如果扩容了之后,容量不够用,新的容量就是所需要的容量,即原始字符串长度加上新添加的字符串长度。
扩容完成之后,将原始数组复制到新的容量中,然后将新的字符串添加进去
StringBuilder
StringBuilder类也代表可变字符串对象。实际上,StringBuilder和StringBuffer基本相似,他们的原理与操作一样,两个类的构造器和方法也基本相同。不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。
StringBuffer类中实现的方法:
public synchronized int compareTo(StringBuffer another) {
return super.compareTo(another);
}
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized int capacity() {
return super.capacity();
}
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
super.ensureCapacity(minimumCapacity);
}
/**
* @since 1.5
*/
@Override
public synchronized void trimToSize() {
super.trimToSize();
}
.........
StringBuilder类中实现的方法:
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(char c) {
super.append(c);
return this;
}
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(int i) {
super.append(i);
return this;
}
@Override
public StringBuilder append(long lng) {
super.append(lng);
return this;
}
@Override
public StringBuilder append(float f) {
super.append(f);
return this;
}
@Override
public StringBuilder append(double d) {
super.append(d);
return this;
}
..........
StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全
StringBuffer StringBuilder 区别
从以下方面来进行说明
1. 是否安全
StringBuffer是线程安全,StringBuilder是线程不安全。因为 StringBuffer 的所有公开方法都是 synchronized修饰的,而 StringBuilder 并没有synchronized修饰。
2. 性能
StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以StringBuilder 的性能要大于 StringBuffer。
StringBuffer 适用于用在多线程操作同一个 StringBuffer 的场景,而 StringBuilder 更适合单线程场合。
3. 缓冲区
StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。 StringBuffer 的这个toString 方法仍然是同步的。
而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
StringBuffer 方法
@Override
@HotSpotIntrinsicCandidate
public synchronized String toString() {
if (toStringCache == null) {
return toStringCache =
isLatin1() ? StringLatin1.newString(value, 0, count)
: StringUTF16.newString(value, 0, count);
}
return new String(toStringCache);
}
StringBuilder 方法
@Override
@HotSpotIntrinsicCandidate
public String toString() {
// Create a copy, don't share the array
return isLatin1() ? StringLatin1.newString(value, 0, count)
: StringUTF16.newString(value, 0, count);
}
String、StringBuffer和StringBuilder的异同
相同点:都是用来代表字符串,底层都是通过char数组实现的。
不同点:
-
String类是不可变类,String对象一旦创建,其值是不能修改的,如果要修改,会重新开辟内存空间来存储修改之后的对象;而StringBuffer和StringBuilder对象的值是可以被修改的;即任何对String的改变会引发新的String对象的生成。
-
StringBuffer和StringBuilder类则是可变类,他俩的原理和操作基本相同,任何对它所指代的字符串的改变都不会产生新的对象。StringBuffer几乎所有的方法都使用synchronized实现了同步,支持并发操作,线程安全,在多线程系统中可以保证数据同步,但是效率比较低;而StringBuilder线程不安全,不支持并发操作,不能同步访问,不适合多线程中使用。但是效率比较高。
-
需要对字符串进行频繁的修改,不要使用String,否则会造成内存空间的浪费。考虑线程安全的场合使用 StringBuffer,如果不需要考虑线程安全,追求效率的场合可以使用 StringBuilder。