1、底层结构
- jdk1.8:private final char[] value;
- jdk1.9:private final byte[] value;
2、String的不可变性
- 字符串常量池里不能存在相同的字符串
- 字符串一旦被赋值则不可修改,后面对字符串的任何修改都是重新new了一个字符串对其做修改
- String的String Pool是一个固定大小的HashTable
- jdk1.6,StringTable默认是1009,可以设置的范围没有限制
- jdk1.7,StringTable默认是60013,可以设置的范围没有限制
- jdk1.8,StringTable默认是60013,可以设置的最小范围是1009
- 通过-XX:StringTableSize设置StringTable的大小
3、字符串拼接操作
- 常量与常量的拼接结果在常量池,原理是编译期优化
- 常量池中不会存在相同内容的常量
- 只要其中有一个是变量,结果就在堆中(StringPool之外的堆中),变量拼接的原理是StringBuilder
- 如果拼接的结果调用Intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址
public static void main(String[] args) {
String s1 = "java";
String s2 = "python";
String s3 = "javapython";
String s4 = "java"+"python";
//如果拼接符号前后出现了变量,则相当于在堆空间中new String(),具体的字符串内容为拼接的结果
String s5 = s1+"python";
String s6 = "java"+s2;
String s7 = s1+s2;
System.out.println(s3==s4);//true
System.out.println(s3==s5);//false
System.out.println(s3==s6);//false
System.out.println(s3==s7);//false
System.out.println(s5==s6);//false
System.out.println(s5==s7);//false
System.out.println(s6==s7);//false
//如果拼接的结果调用Intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址
String s8 = s6.intern()
System.out.println(s3==s8);//true
}
4、StringBuilder.append()和String字符串拼接比较
public static void methodString(int n) {
String s = "";
for (int i = 0; i < n; i++) {
s = s + "a";
}
}
public static void methodStringBuilder(int n) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < n; i++) {
s.append("a");
}
}
public static void main(String[] args) {
int n = 100000;
long start1 = System.currentTimeMillis();
methodString(n);
long end1 = System.currentTimeMillis();
System.out.println("methodString:" + (end1 - start1)); //methodString:3454
System.out.println("---------------------");
long start2 = System.currentTimeMillis();
methodStringBuilder(n);
long end2 = System.currentTimeMillis();
System.out.println("methodStringBuilder:" + (end2 - start2)); //methodStringBuilder:1
}
通过上面测试比较,可以发现StringBuilder比String要快得多
- StringBuilder.append()只会创建一个StringBuilder对象,String字符串拼接会创建多个StringBuilder对象和String对象
- String字符串拼接会创建多个StringBuilder对象和String对象产生,会占用大量内存,如果要进行GC,会花费额外的时间
5、String.intern()的使用
- intern()会判断常量池里有没有这个字符串,有的话这该变量的引用地址返回,没有的话在常量池里添加该字符串并返回其引用地址
- String s = new String(“ab”)创建了几个对象? -----字符串常量池中有"ab"
- 两个,一个是new关键字在堆里创建的,另一个在字符串常量池中,字节码指令:ldc
- String s = new String(“a”) + new String(“b”)创建了几个对象? -----字符串常量池中没有"ab"
- 6个,对象一:堆中的StringBuilder,对象二:堆中的"a",对象三:字符串常量池中的"a",对象四:堆中的"b",对象五:字符串常量池中的"b"
- 深入剖析:对象六:堆中的new String(“ab”),toString()方法的调用,底层虽然是new String(),但是只会在堆中创建对象,不会在字符串常量池中生成字符串
- 对于程序中大量存在的字符串,尤其其中存在很多重复的字符串时,使用String.intern()方法可以节省内存空间