字符串常量池
字符串常量池在运行时常量池中,jdk7开始字符串常量池则被移入到堆中,就是说jdk7开始字符串常量池和常量池分开了。
当在双引号””中有字面量时,就会在串池创建一个该对象,如出现“a”,就会在串池中有个”a”,又如String s = “b”;那么串池中就有个”b”,这里注意,除了后面说的一种特殊情况,其它正常来说只要在双引号有字面量,就会在串池创建对象,如String s = new String(“c”);这里既有双引号,又有关键字new,就会在串池和堆中分别创建”c”和一个字符串对象”c”,这句话创建了两个对象!但s的引用是指向堆中的字符串对象。
当要在串池中创建对象时,会去检查串池中有没有该对象,没有才创建,如String s1 = “a”;String s2 = “a”,则s1和s2的地址是一样的,都是指向串池中的”a”,但如果是String s1 = new String(“a”);String s2 = new String(“a”),则s1和s2的地址是不一样的,因为两者分别指向堆中的两个不同的字符串对象,当然,在创建s1的时候在串池中创建”a”,但创建s2时发现串池中已经有”a”了,就不会再创建。
字符串拼接有两种,一种是字符串常量拼接,另一种是字符串变量拼接。当加号+左右都是常量时才是常量拼接,当出现一个变量或加号左右都是变量时就是变量拼接
编译器在编译期间(javac)会先把所有常量拼接好,换句话说class文件中只有变量拼接。先说字符串常量拼接吧,字符串常量拼接原理是编译器优化,如有String s1 = “ab”;String s2 = “a” + “b”;这里s2是由两个常量拼接来的,在编译器这句话就会变成String s2 = “ab”;也就是说在class文件就没有”a”和”b”了,这就是之前说的特殊情况,java文件中的双引号中有字面量,但不会在串池中创建对象,因为被编译器优化了,实际上在class文件中没有”a”和”b”,故这里s1和s2都是串池中的”ab”,是同一个串池对象。
再说字符串变量拼接,字符串变量拼接原理是StringBuilder,就是说有String s1 = “a”;String s2 = “b”;String s3 = s1 +s2;那么这句话本质其实是,String s3 = new StringBuilder().append(“a”).append(“b”).toString();可以理解为String s3 = new String(“ab”),但这里不会往串池中放入”ab”,因为这是拼接后的结果,class文件中没有”ab”。最终结果是:串池中有”a”,”b”,堆中有个字符串对象”ab”,过程中出现了个StringBuilder对象。当然,串池从jdk7开始也是在堆中,但只是堆单独划出的一部分,没有和堆融合。再补充一点,被final修饰的变量可以当常量处理。
那么,String s = new String(“a”) + new String(“b”);这句话创建了几个对象呢,首先有双引号,双引号中有字面量”a”,”b”,所以会在串池中创建两个对象”a”,”b”,因为还有两个new,所以会在堆中创建两个字符串对象,对象的值也分别为”a”,”b”,但和串池中的不是同一个,一个在串池,一个在堆中。又因为这是字符串变量拼接,所以还有new一个StringBuilder对象,最后结果是String s = new String(“ab”);又在堆中创建了一个字符串对象”ab”,所以一句话总共创建了六个对象,串池中”a”,”b”,堆中”a”,”b”,”ab”还有一个StringBuilder。
String s = new String(“a”) +”a”+”b”;这句话创建了几个对象?5个(串池中a,ab,堆中a,aab,stringBuilder)
intern方法会将一个字符串对象主动放入串池:
- 如果串池中已经有这个字符串:
那么就不会再放入,并返回串池中该对象的引用。
- 如果串池中没有该字符串:
- jdk7以前:新建一个字符串对象放入串池,也就是说串池和堆中的对象不是同一个,然后会返回串池里的那个的引用。
- jdk7开始:把堆中的引用放入串池,即串池和堆中的对象是同一个,方法返回串池中的引用。即这里串池中的地址,堆中的地址和返回的地址是同一个,都源自于堆中的那个对象。
比如有String s1 = “a”;String s2 = s1.intern();那么s1==s2的,因为串池中已经有”a”了,intern()返回的是串池中的引用。
再比如String s1 = new String(“a”) + new String(“b”);String s2 = s1.intern(),在jdk7以前,s1 != s2,因为intern是串池中创建新的对象,两个”ab”是不同的;jdk7开始s1==s2,因为intern是把堆中的地址放入串池,串池和堆中的”ab”是同一个对象。