JAVA 字符串详解
字符串创建有两种基本形式:
1、直接赋值:String s1 = "ab";
此操作只会在字符串常量池创建一个“ab"字符串
原因:该值能在编译期就直接确定,因此只会在常量池创建。
2、用new String来创建一个字符串
此时分两种情况:
(1)字符串常量池中已经有和这个将要创建的字符串的字面量相等(.equal()返回true)的字符串,则只在堆中生成一个字符串对象。
String s1 = "ab";
String s2 = new String(“ab”);
两次ldc都是#2对应的字符串,即两次创建只在常量池中生成一个“ab”。
(2)字符串常量池中没有和这个将要创建的字符串的字面量相等的字符串:
此时会生成两个字符串对象,一个在堆中,一个在常量池。
(在此之前ldc索引只到16,而这里变成17说明常量池中也生成了一个字符串)。
3、字符串拼接
分三种情况:
(1)String s10 = "abc" + "d";
直接用字面量拼接的话,只会在常量池中生成一个字符串对象,且是拼完后的结果,拼之前的两个字符串不会在常量池中创建。
原因:本质上是先进行了字符串拼接操作,得到这个字面量,然后因为编译期就能够确定这个字面量,所以只在常量池创建。
(2)String s4 = new String("ad") + new String("c");
总共创建了5个字符串对象,“ad“和”c”分别在堆中和在常量池中创建对象。但拼完的“adc”只在堆中而不在常量池中。
原因: 只要使用了构造器或引用方法来创建对象,那么就无法在编译期确定该字符串的值,因此会在运行期在堆中和常量池中创建。
(3)String s17 = new String("abcge") + "k";
生成四个字符串对象,“abcge”在堆中和字符串常量池中都创建,“k”只在常量池创建,拼完的“abcgek”只在堆中。
(4) String s18 = "ab";
String s19 = "cd";
String s20 = s19 + s18;
此种形式s20只会在堆中创建一个对象”abcd".
与情况1对比:对于情况1,java是直接将两个字符串拼接后的字符串存入常量池,原因是在编译期,这个字符串的值就已经确定,因此java直接对其优化,将该字符串存入常量池;而对于情况4,引用类型只有在运行时才能确定,因此虽然s18,s19在常量池中存了“ab"和”cd",但由于拼接时没有直接使用字面量,而是通过引用进行拼接,那么此时就只会在堆中创建一个“abcd"。
如果将s18,s19变为常量,那么此时s20就会将”abcd“存入常量池:
final String s18 = "ab";
final String s19 = "cd";
String s20 = s19 + s18;
String s21 = "ab" +”cd";
System.out.println(s20==s21); //返回true
原因:用final修饰的变量在定义的时候直接进行初始化,那么就会在编译器进行”宏替换“,即变成直接量,因此当进行引用拼接时,s18和s19直接在编译的时候被替换为“ab"与”cd"
##########################################################################
字符串拼接的实现原理是(非情况(1)时):
(1)先new一个空的StringBuilder对象
(2)将两个子串通过StringBuilder的append方法存到StringBuilder中
(3)最后通过StringBuilder的toString方法返回一个拼接完的String.
#########################################################################
Intern方法:
分两种情况:
(1)若常量池中有和调用此方法的字符串对象字面量相等的字符串,则返回常量池中的字符串对象的引用。
此例中,“ab“已在常量池中,调用intern返回常量池中”ab“的引用,因此s3==s1返回true.
(2)若常量池中没有的话,就返回当前字符串的引用
此例中,由于拼接之后的字符串不会在常量池创建,因此调用intern方法将把这个字符串放入常量池,并返回该字符串的引用(并不是在常量池中创建了一个新的“adc”,只是把堆中“adc”的引用放到常量池中,依然指向堆中的”adc”),因此s7==s6返回true.
如果此时用赋值法:String s8 = "adc";则会直接返回堆中“adc“的引用。
Intern方法的描述中我们可以看到对上述过程的描述。
注意,当常量池中没有该对象时,是“this String object is added to the pool and a reference to this String object is returned”,这里的add其实只是把引用放到常量池中,而并没有将“adc”从堆中移动到常量池。
总结:
单个new会创建2个对象,一个在堆,一个在常量池。
N个new操作,然后拼接会生成2*N+1个对象,每个new生成两个,一个在堆一个在常量池,最后拼出来的生成一个在堆中。
直接赋值都只会在常量池中创建一个。
直接赋值 拼接new : 直接赋值在常量池中创一个,new在堆和常量池各创一个,最后拼接会在堆中创一个,总共4个。