1、概念:
1、String table又称为String pool,字符串常量池,其存在于堆中(jdk1.7之后改的)。最重要的一点,String table中存储的并不是String类型的对象,存储的而是指向String对象的索引,真实对象还是存储在堆中。
2、此外String table里面不存在相同的两个字符串。
3、此外String对象调用intern()方法时,会先在String table中查找是否存在于该对象相同的字符串,若存在直接返回String table中字符串的引用,若不存在则在String table中创建一个与该对象相同的字符串。
2、StringTable在内存中的位置:(存在堆内存中JDK1.8,JDK1.6在永久代的常量池中)
3、常量池在内存中的位置:
4、面试题代码:
public class StringTest {
public static void main(String[] args) {
// StringTable串池 ["a","b","ab"]
String s1 = "a";
String s2 = "b";
String s3 = "a" + "b";
String s4 = s1 + s2;
String s5 = "ab";
String s6 = s4.intern();
// 问
System.out.println(s3 == s4); // false
System.out.println(s3 == s5); // true
System.out.println(s3 == s6); // true
// 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢
String x2 = new String("c") + new String("d");
String x1 = "cd";
x2.intern();
System.out.println(x1 == x2); // false
String x2 = new String("c") + new String("d");
x2.intern();
String x1 = "cd";
System.out.println(x1 == x2);// true,如果是JDK1.6,此时就是false,
}
}
5、原理解释:
public class StringTest {
public static void main(String[] args) {
// StringTable串池 ["a","b","ab"]
String s1 = "a";
// 常量池中的字符串仅是符号,第一次用到时才变为对象 ,没用到的时候就在常量池中,第一用到时才变为对象,
// 如果此时串池(StringTable)中没有"a",就直接存放在串池中,如果串池中有"a",就返回"a"的对象给s1,利用串池的机制,来避免重复创建字符串对象
String s2 = "b";
// 字符串常量拼接,字符串常量拼接的原理是编译期优化 ,如果串池中有"ab",就直接将引用指向该"ab",如果没有就将"ab"对象放入串池中
String s3 = "a" + "b";
// 字符串变量拼接的原理是 StringBuilder (1.8),底层调用StringBuilder,
// 分别将s1(a)和s2(b)拼接到一起,并创建一个新的String对象 ,在堆中存放,并没有放到串池中
String s4 = s1 + s2;
// 此时串池中已经有了"ab",所以就直接将"ab"(s3)的对象返回给了s5,所以s3和s5的地址是相等的
String s5 = "ab";
// 此时s4对象调用intern()试图将s4对象放入串池,由于串池中已经有了"ab"对象,所以没能将s4对象放入串池中,s4此时还是存放在堆中,
// 而是将串池中的"ab"(s3)对象返回给了s6,所以s3的地址和s6的地址相同。
String s6 = s4.intern();
// 问
System.out.println(s3 == s4); // false
System.out.println(s3 == s5); // true
System.out.println(s3 == s6); // true
// StringTable串池 ["a","b","ab","c","d","cd"]
// 此写法是先将字符串常量"c","d"放入常量池中,再new对象,最后通过StringBuilder来将字符串拼接,最后创建String("cd"),其对象放在堆内存中
// String x2 = new String("c") + new String("d");
// 由于此时串池中没有"cd",就直接将"cd"(x1)存放到串池中,
// String x1 = "cd";
// x2对象调用intern()来将x2存放到串池中,但是此时串池中已经有了"cd",故没有将X2对象存入串池中,此时X2对象存放在堆中,所以X1和X2的地址不相等。
// x2.intern();
// System.out.println(x1 == x2); // false
// 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢 System.out.println(x1 == x2);
// StringTable串池 ["a","b","ab","c","d","cd"(X2)]
// 此写法是先将字符串常量"c","d"放入常量池中,再new对象,最后通过StringBuilder来将字符串拼接,最后创建String("cd"),其对象放在堆内存中
String x2 = new String("c") + new String("d");
// X2调用intern方法试图将X2对象放入串池中,由于串池中没有"cd",故直接将X2("cd")存入串池中,X2的地址现在串池中
x2.intern();
// 此时串池中已经"cd"(X2),因此没有将这个"cd"存入串池中,而是将X2("cd")的对象返回给了X1,所以X1的地址就和X2的地址相等
String x1 = "cd";
System.out.println(x1 == x2); // true,如果是JDK1.6,此时就是false,因为,X2在调用intern()时,串池中没有"cd",就将X2的对象复制一份存入到串池中
// 此时串池中的存的是X2复制的新对象,String x1 = "cd";执行这行代码时,由于串池中存着X2复制的新对象"cd",所以将这个新对象的地址返回给了X1,所以最终X1和X2的地址不相等。
//可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池
//1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串 池中的对象返回
//1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份, 放入串池, 会把串池中的对象返回
}
}