String:字符串常量,字符串长度不可变。Java 中 String 是 immutable(不可变)的
String的底层代码为一个用final修饰的char数组,定义一个String变量以后,该变量的内容是不可变
我们可以查看源码如下:
public final class String
implements java.io.Serializable, Comparable, CharSequence {
/** The value is used for character storage. */
private final char value[];
......
}
因为在Java中字符串不可变,每次使用 “+” 拼接字符串都会在字符创常量池中产生新的字符换,
而且这三个对象一直都未失去引用,所以JVM无法进行垃圾回收,这样会导致占用大量方法区内存,造成内存资源的大量浪费。
如:
String str = "hello" + "world";
上面一行创建字符串的代码就创建了3个字符串对象,"hello","world","helloworld"
String和StringBuffer,StringBuilder对象内存图
StringBuffer:字符串变量(Synchronized,即线程安全)。频繁对字符串内容进行修改,出于效率考虑最好使用 StringBuffer,如果想转成 String 类型,可以调用 StringBuffer 的 toString() 方法。
源码:
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized int capacity() {
return value.length;
}
@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;
}
}
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
在源代码中会看到很多方法都使用了 “synchronized ” 关键字修饰,因此也是线程安全的!
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
StringBuffer和StringBuilder都是继承自AbstractStringBuilder抽象类
StringBuilder:字符串变量(非线程安全)。方法用法和StringBuffer差不多
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
通过底层代码我们可以看到String 是不可变的对象, 因此在每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,性能就会降低。
StringBuilder 与StringBuffer在拼接字符串时,通过判断字符串长度是否足够,从而创建一个新的数组用来封装数据,而原来的对象被放弃引用,等待垃圾回收,从而减少内存的浪费
1、基本原则:
(1)如果要操作少量的数据,用String
(2)单线程操作大量数据,用StringBuilder
(3)多线程操作大量数据,用StringBuffer
2、不要使用String类的"+"来进行频繁的拼接,为那样的性能极差的,应该使用StringBuffer或StringBuilder类
3、为了获得更好的性能,在构造 StringBuffer 或 StringBuilder 时应尽可能指定它们的容量。如果操作的字符串长度(length)不超过 16 个字符就不用了,当不指定容量(capacity)时默认构造一个容量为16的对象。不指定容量会显著降低性能。
4、StringBuilder 一般使用在方法内部来完成类似 + 功能,因为是线程不安全的,所以用完以后可以丢弃。StringBuffer 主要用在全局变量中。
5、相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。而在现实的模块化编程中,负责某一模块的程序员不一定能清晰地判断该模块是否会放入多线程的环境中运行,因此:除非确定系统的瓶颈是在 StringBuffer 上,并且确定你的模块不会运行在多线程模式下,才可以采用 StringBuilder;否则还是用 StringBuffer。
参考文章
Java String、StringBuffer 和 StringBuilder 的区别