JDK 8 下的String
字符常量池
jdk8 位于堆中!
创建对象的两种方式
通过字面量创建
String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2);
采用字面值的方式创建一个字符串时,JVM首先会去字符串池中查找是否存在”abc”这个对象,如果不存在,则在字符串池中创建”abc”这个对象,然后将池中”abc”这个对象的引用地址返回给”abc”对象的引用s1,这样s1会指向池中”abc”这个字符串对象;如果存在,则不创建任何对象,直接将池中”abc”这个对象的地址返回,赋给引用s2。因为s1、s2都是指向同一个字符串池中的”abc”对象,所以结果为true。
new关键字创建
String s1 = new String("xyz");
String s2 = new String("xyz");
System.out.println(s1==s2);
采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有”xyz”这个字符串对象,如果有,则不在池中再去创建”xyz”这个对象了,直接在堆中创建一个”xyz”字符串对象,然后将堆中的这个”xyz”对象的地址返回赋给引用s3,这样,s3就指向了堆中创建的这个”xyz”字符串对象;如果没有,则首先在字符串池中创建一个”xyz”字符串对象,然后再在堆中创建一个”xyz”字符串对象,然后将堆中这个”xyz”字符串对象的地址返回赋给s3引用,这样,s3指向了堆中创建的这个”xyz”字符串对象。s4则指向了堆中创建的另一个”xyz”字符串对象。s3 、s4是两个指向不同对象的引用,结果当然是false。
new String(“s”)涉及两个对象:字符串常量池和堆中各一个。
若规定new 一次算创建一个对象 则通过javap -c 查看字节码,只创建了一个对象
Compiled from "StringTest.java" public class StringTest { public StringTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class java/lang/String 3: dup 4: ldc #3 // String a 6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V 9: astore_1 10: return }
intern()
When the intern method is invoked, if the pool already contains a
string equal to this
String
object as determined bythe {@link #equals(Object)} method, then the string from the pool is
returned. Otherwise, this
String
object is added to thepool and a reference to this
String
object is returned.
- String.intern()是一个Native方法,它的作用是:如果运行时常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则在常量池中创建与此String内容相同的字符串,并返回常量池中创建的字符串的引用。
几个问题
注意:每个问题的代码均独立运行互不干扰
1
String a = new String("1")+new String("2");
a.intern();
String b = "12";
System.out.println(a==b);
true;
解释:
- a对象通过+创建,所以栈中a 引用堆中的String(”12”)对象。
- a.intern()方法被调用,字符常量池中 “12” 引用堆中的String(”12”)对象。
- b是通过常量直接创建的,而字符常量池中有“12” 直接返回常量池中对象的引用(堆中的String(”12”)对象)。
- a==b 就是比较引用是否相等,都是 引用堆中的String(”12”)对象所以相等。
2
String a = new String("1")+new String("2");
String b = "12";
a.intern();
System.out.println(a==b);
false;
a.intern();的调用位置发生了变化,显然b中指向的是字符常量池中的对象,而a指向的是堆中的对象,所以false
3.
String b = new StringBuilder("abx").toString();
System.out.println(b.intern()==b);
false
解释见下
4.
String b = new StringBuilder("ab").append("x").toString();
System.out.println(b.intern()==b);
true
*解释:3:*new StringBuilder(“abx”); 执行该语句就会在字符串常量池中创建一个对象abx*,堆中也创建了一个对象b*。既然常量池中已经有了abx,那么在叫用intern()方法的时候就会返回常量池中的abx,而b是堆中的一个对象的引用。显然不相等。
**4:**String b = new StringBuilder(“ab”).append(“x”).toString();
常量池中至少(还有很多默认加入的对象)有ab和x,没有abx!调用intern就会返回b,当然相等。
5.
String b = new StringBuilder("ja").append("va").toString();
System.out.println(b.intern()==b);
false
因为java会被默认加入到常量池中 rt.jar
参考
https://www3.ntu.edu.sg/home/ehchua/programming/java/J3d_String.html