字符串常量池
字符串常量池:在jdk1.8中是堆空间的一个常量池,用于存放已经创建的字符串。在字符串池中的每个字符串对象只有唯一的一份,可以被多个引用所指向,避免了重复创建内容相同的字符串;通过字面值赋值创建的字符串对象存放在字符串池中,通过关键字new出来的字符串对象存放在堆中(也在常量池中创建该字符串的常量,只是返回的是地址是指向堆中的对象)。
创建字符串分析
使用字面量方式创建
String s1 = "A";//会直接在字符串常量池中创建常量"A",然后返回
String s2 = "A";//会直接返回字符串常量池中的常量"A",
System.out.println(s1 == s2);
//将字符串常量池中"AA"对应的成堆中的引用
String s3 = new String("A") + new String("A");
System.out.println(s3 == s3.intern()); //true,此时字符串常量池存在"AA"对应的堆引用
String s4="AA";
System.out.println(s3==s4);//true
JVM会判断字符串常量池中是否存在该常量,如果存在该常量,那么会直接返回字符串常量池中的常量;如果存在该常量在堆中的引用,那么会返回该引用(指向堆中的对象);如果字符串常量池中不存在该字符串字面量,那么会在常量池中创建该字符串的常量,并返回字符串池中的常量。
上述代码因为s1、s2储存的都是常量池中"A"这个常量对象的地址,所以,s1==s2返回true。
该方法最多只会创建一次对象。堆空间不存在对象,字符串常量池可能会创建一个常量。
用该方式创建的对象s1,只会在字符串常量池中。
使用new关键字方式创建
String s5=new String("A");
String s6=new String("A");
System.out.println(s5==s6);//false
1.无论堆上是否存在相同字面量的对象,JVM会先在堆上创建一个对象。
2.JVM会判断字符串常量池中是否存在该字符串对象的字面量,如果存在(不管是存在常量还是存在堆中的引用),就什么也不做。如果不存在,那么会在常量池中创建该字符串的常量。
3.返回该字符串在堆中创建对象的引用。
上述代码因为s3、s4储存的是堆空间新分配的地址,所以,s3==s4返回false。
该方法最多会创建两次对象,肯定会在堆空间中创建,还可能会在字符串常量池中创建一次常量。
用该方式创建的对象s5,会在堆空间和字符串常量池中。
使用字面量相加方式创建
//常量池上存在常量AB
String s6 = "AB";
String s7 = "A" + "B";
System.out.println(s6 == s7); //true
//常量池上存在引用CD
String s8 = new String("C") + new String("D");
s8.intern();
String s9 = "C" + "D";
System.out.println(s8 == s9); //true
和使用字面量方式创建类似。
1.JVM会分别判断字符串常量池中是否存在"A"、"B"常量,如果存在(不管是存在常量还是存在堆中的引用),那就什么也不做;如果不存在,那么会在常量池中创建其字符串的常量。
2.JVM会分别判断字符串常量池中是否存在"AB"常量,如果存在该常量,那么会直接返回字符串常量池中的常量;如果存在该常量在堆中的引用,那么会返回该引用(指向堆中的对象);如果字符串常量池中不存在该字面量,那么会在常量池中创建该字符串的常量,并返回字符串池中的常量。
s7在创建时最多会在字符串池中创建三个常量。
用该方式创建的对象s7,只会在字符串常量池中。
使用new关键字相加方式创建
String s10 = new String("AA") + new String("B");
String s11 = new String("AA");
System.out.println(s11 == s11.intern());//false
System.out.println(s10 == s10.intern());//true
1.无论堆上是否存在相同字面量的对象,JVM会先在堆上分别创建"AA"、“B”、"AAB"三个对象。
2.JVM会分别判断字符串常量池中是否存在"AA"、"B"对象的字面量,如果存在(不管是存在常量还是存在堆中的引用),那么不做任何操作。如果不存在,那么会在常量池中创建其字符串的常量。
3.返回堆空间中"AAB"对象的引用。
s10在创建时最多会创建五个对象,其中堆中三个肯定有,还可能会在字符串常量池中创建两个常量。
用该方式创建的对象s10,只会在堆空间中。
使用字面量和new关键字相加方式创建
String s12 = "A" + new String("B");
System.out.println(s12 == s12.intern());//true
String s13="AB";
System.out.println(s12 == s13); //true
1.JVM会判断字符串常量池中是否存在"A"常量,如果存在,就什么也不做,如果不存在,会在字符串常量池中创建"A"常量;
2.无论堆上是否存在相同字面量的对象,在堆空间中创建"B"对象,然后判断字符串常量池中是否存在"B"常量,如果存在,就什么也不做,如果不存在,会在字符串常量池中创建"B"常量;
3.无论堆上是否存在相同字面量的对象,在堆空间中创建"AB"对象并将其地址返回给s12。
s12在创建时最多会创建四个对象,其中堆空间肯定会有两个,字符串常量池可能会创建两个常量。
用该方式创建的对象s12,只会在堆空间中。
String.intern()
String s14 = "A";
System.out.println(s14 == s14.intern()); //true
String s15 = new String("A") + new String("A");
String s16 = new String("A") + new String("A");
s15.intern();//此时字符串常量池中"AA"对应的是堆中s15的引用
System.out.println(s15 == s16.intern());//true
System.out.println(s16 == s16.intern());//false
String s17 = new String("B") + new String("B");
System.out.println(s17 == s17.intern()); //true
intern()方法在jdk1.8中效果如下:
JVM会判断这个字符串对象字面量是否存在于常量池中,如果字符串常量池中存在该常量,那么会直接返回字符串常量池中的常量;如果字符串常量池中存在该常量在堆中的引用那么会返回该引用(指向堆中的对象);如果字符串常量池中不存在该字面量,那么会将当前对象引用复制到常量池。并且返回的是当前对象的引用。