对java.lang.String#intern方法的理解
先看一下关于intern()的几个案例,本文以jdk1.8为例:
第一组:
案例1.
public static void main(String[] args) {
String s1 = new String("abc") + new String("whl");
// String s2 = "abcwhl";
System.out.println(s1.intern() == s1);
// System.out.println(s1 == s2);
}
打印结果:
true
案例2.将这两行注释的代码打开
public static void main(String[] args) {
String s1 = new String("abc") + new String("whl");
String s2 = "abcwhl";
System.out.println(s1.intern() == s1);
System.out.println(s1 == s2);
}
打印结果:
false
false
第二组:
案例3.将上述代码做一下调整:
public static void main(String[] args) {
String s1 = new String("abc") + new String("whl");
// System.out.println(s1.intern() == s1);
String s2 = "abcwhl";
System.out.println(s1 == s2);
}
打印结果:
false
案例4.将案例3中的注释打开
public static void main(String[] args) {
String s1 = new String("abc") + new String("whl");
System.out.println(s1.intern() == s1);
String s2 = "abcwhl";
System.out.println(s1 == s2);
}
打印结果:
true
true
那我们来扒一下intern()方法的源码
/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
* <p>
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java™ Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();
该方法以native关键字修饰,代码该方法是本地方法,我们是看不碰上它实现的详细源码了。从注释中可以了解了一下该方法的作用:当某字符串对象调用该方法时,首先判断常量池中是否已经存在该字符串(调用equals(Object)方法),如果池中已存在该字符串,则返回池中的该字符串的引用;如果常量池中不存在该字符串,则将该对象放入常量池,并返回该对象的引用。@return 标签的解释为返回一个与该字符串内容相同的一个字符串,也就是说如果该对象是{@code new String(“abc”) + new String(“whl”)},则返回"abcwhl"。注意,这里判断是否存在该字符串,使用的String的equals()方法,即字符串常量池中有与当前对象的值相同的字符串(或对象),就代码字符串常量池中存在该对象。
那我们根据源码上的注释的解释来对案例1-4进行分析如下:
图解分析案例1:
1)创建s1,创建一个对象{code new String(“abc”) + new String(“whl”)},这个对象的地址值是在堆中,{code “abc”}和{code “whl”}分别在字符串常量池中。
2)s1调用Intern()方法,则将使用this.equals()方法判断此时字符串常量池中是否存在与该对象的字符串内容相同的字符串,此时没有,则将该对象放入字符串常量池中,并将该字符串的引用返回,也可以认为返回的字符串的内容是"abcwhl"。那么此时s1.intern()返回的地址值与s1是相同的,所以s1.intern()==s1返回true。
图解分析案例2:
1)创建s1,创建一个对象{code new String(“abc”) + new String(“whl”)},这个对象的地址值是在堆中,{code “abc”}和{code “whl”}分别在字符串常量池中。
2)创建s2,去字符串常量池中查询是否存在与"abcwhl"有相同内容的字符串,没有,则创建字符串"abcwhl",并将该对象的引用返回给s2。
3)此时s1调用intern()方法,则将使用this.equals()方法判断此时字符串常量池中是否存在与该对象的字符串内容相同的字符串,此时有,则只将"abcwhl"字符串返回,将不会将{code new String(“abc”) + new String(“whl”)}这个对象放入字符串常量池中。所以s1.intern()指向常量池中的"abcwhl"字符串,而s1指向的是堆的new出来的对象{code new String(“abc”) + new String(“whl”)}。所以s1.intern()==s1返回true。
同理,很明显s1==s2返回false。
图解分析案例3:
1)创建s1,创建一个对象{code new String(“abc”) + new String(“whl”)},这个对象的地址值是在堆中,{code “abc”}和{code “whl”}分别在字符串常量池中。
2)创建s2,去字符串常量池中查询是否存在与"abcwhl"有相同内容的字符串,没有,则创建字符串"abcwhl",并将该对象的引用返回给s2。
很明显,s1和s2的没有指向同一下地址,所以s1==s2返回false
图解分析案例4:
1)创建s1,创建一个对象{code new String(“abc”) + new String(“whl”)},这个对象的地址值是在堆中,{code “abc”}和{code “whl”}分别在字符串常量池中。
2)s1调用Intern()方法,则将使用this.equals()方法判断此时字符串常量池中是否存在与该对象的字符串内容相同的字符串,此时没有,则将该对象放入字符串常量池中,并将该字符串的引用返回,也可以认为返回的字符串的内容是"abcwhl"。那么此时s1.intern()返回的地址值与s1是相同的,所以s1.intern()==s1返回true。
3)创建s2,去字符串常量池中查询是否存在与"abcwhl"有相同内容的字符串,有,则将字符串常量池中的地址引用返回给s2"abcwhl",并将该对象的引用返回给s2。
很明显,此时s2与s1指向的是一个地址,所以s2==s1返回true。