public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
省略一万个字
}
从jdk源码中可以发现,String类使用字符数组保存字符串(char value[]),同时被final关键字修饰,所以String对象是不可变的。下面代码中两个字符串变量的名称不同,但从输出结果发现,他们竟然有相同的hashcode。这是因为String变量在内存中是共享的,变量a与b指向的是同一块内存。这里很奇怪,前面为什么说String对象是不可修改的,但是却可以通过‘+=’来改变a中的内容。其实jdk是通过重新开拓一片内存,存放计算之后的数据。再将这块区域的引用给a对象。通过运行结果可以看出,a在进行了计算之后,hashcode发生了改变,并非其值变化了,而是引用变化了。
public static void main(String[] args) {
String a = "12";
String b = "12";
System.out.println(a.hashCode());
System.out.println(b.hashCode());
a+="1";
System.out.println(a);
System.out.println(a.hashCode());
}
//输出结果如下
1569
1569
121
48688
Process finished with exit code 0
总结,String是常量并且在内存中是共享的,因为是常量,所以String的线程是安全的。(强制安全)。
而StringBuilder和StringBuffer都继承于AbstractStringBuilder类。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
//此处省略一万个字
}
AbstractStringBuilder类也是通过字符数组存储字符串,但是却没有final修饰关键字,因此StringBuilder和StringBuffer是可变字符串,和普通对象无异。由于重写了其父类中的许多方法如toString,length,insert,append。因此StringBuilder和StringBuffer的操作比String更灵活,实际应用范围也更广。
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
StringBuilder和StringBuffer最大的区别在于StringBuffer是线程安全的,StringBuilder的线程不安全。因为StringBuffer内部的方法加了synchronized修饰,成为了同步方法。若对数据安全性要求不高,从性能和轻量级的角度来看 建议使用StringBuilder。对于安全性要求很高的数据必须使用StringBuffer。
public synchronized StringBuffer append(StringBuffer sb) {
toStringCache = null;
super.append(sb);
return this;
}
/**
* @since 1.8
*/
@Override
synchronized StringBuffer append(AbstractStringBuilder asb) {
toStringCache = null;
super.append(asb);
return this;
}
明天将再从源码的角度,谈谈synchronized的底层原理,为什么会影响性能,以及锁升级的相关概念,请大家继续关注。