先简单介绍几个方法:
public native String intern();
当调用 intern 方法时,如果池中已经包含一个等于该 String 对象的字符串,由 equals(Object) 方法确定,则返回池中的字符串。否则,将此 String 对象添加到池中并返回对该 String 对象的引用。
由此可见,对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为真时,s.intern() == t.intern() 才为真。
public String(String original) { this.value = original.value; this.hash = original.hash; }
初始化一个新创建的 String 对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。除非需要原始的显式副本,否则不需要使用此构造函数,因为字符串是不可变的。
这个方法也就是平时使用new关键字创建字符串调用的构造器
废话不说直接代码测试
第一种情况:直接使用双引号创建字符串
public void stringtest(){
String str1="aismy";
String str2="aismy";
String str3=new String("aismy");
System.out.println(str1==str2);
System.out.println(str1==str3);
System.out.println(str1==str3.intern());
}
结果:true、false、true
分析原因:在String str1="aismy";时,将"aismy"添加到字符串常量池中,因此创建str2直接返回常量池中地址,因此str1==str2为true;而使用new 关键字创建的str3会重新在堆中创建一个String对象,因此str1和str3一个在常量池中一个在堆中,str1==str3为false;而str3.intern(),即将str3尝试放入常量池中,但是此时常量池已存在该字符串(在str1时就放入了),因此str3.intern()直接返回的是常量池中的该字符串地址,也就是str1的地址,因此str1==str3.intern()为true;
第二种情况:使用new关键字创建字符串对象
public void stringtest(){
String str1=new String("aismy");
System.out.println(str1.intern()==str1);
}
结果:false
分析原因:在使用String str1=new String("aismy");创建str1对象时,此时常量池没有该字符串因此会创建两个对象,一个就是str1对象,除此之外再创建一个对象然后放入常量池中。
假设上述分析错误,也就是String str1=new String("aismy");并没有将“aismy”字符串放入常量池中,那么str1.intern()==str1应该返回true,因为若常量池中没有该字符串,那么调用str1.intern()也就是将str1引用地址放入常量池中,而返回结果为false,显然在调用str1.intern()之前,该字符串已存在与常量池中。
第三种情况:使用intern()
public void stringtest(){
String str1=new StringBuilder("ai").append("smy").toString();
System.out.println(str1.intern()==str1);
}
结果:true
因为new StringBuilder("ai").append("smy")并没有将“aismy”放入常量池中,因此调用str1.intern()就是将str1的引用放入常量池中。
补充:jkd1.7之后字符串常量池被迁移至堆中,那么只需在常量池里记录首次出现的实力引用即可,也就是情况三中直接将str1引用放入常量池中。而在1.6中字符串常量池在方法区中(永久代)因此使用intern()会把首次遇到的字符串实例复制到永久代的字符串常量池中,返回的也是永久代里面的字符串实例,此时情况三结果为false
小结:情况二使用new关键字调用string构造器,会先判断该字符串在常量池中是否存在,不存在则会创建两个对象,而情况三则是调用intern()将字符串放入常量池中
本文参考:《深入理解java虚拟机》
错误之处,希望在评论区指出。