String用于提升性能的intern()方法
JDK每次升级都会做很多优化,我们使用最多的String常量类也在不断被优化。这次和大家分享的是JDK1.8中对String的优化之一,intern()方法的使用。
对应的方法及注释如下:
/**
* Returns a canonical representation for the string object.
*
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
*
* 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.
*
* 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}.
*
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* The Java™ Language Specification.
*
* @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();
一句话概括,就是通过常量池复用来节省内存空间、减少开销以提升性能。
Tips:
这里简单介绍一下常量池,方便下文理解。先看看JVM的内存结构:
我们的常量池就在方法区中,在实际应用中会跟堆区配合使用。平时大家都统称它为常量池,严格划分的话常量池又分为:静态常量池、运行时常量池和字符串常量池。三者的区别,有时间会写一篇单独的博客。
回到今天的主题,intern()方法。如果我们对着英文注释一句一句翻译,来理解它的话会很有限很苦涩,结合代码示例理解会更好一些(个人经验)。
示例一:
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc");
String str3 = str2.intern();
System.out.println("str1==str2: " + (str1 == str2)); // false
System.out.println("str2==str3: " + (str2 == str3)); // false
System.out.println("str1==str3: " + (str1 == str3)); // true
}
运行结果:
str1==str2: false
str2==str3: false
str1==str3: true
分析,str2.intern()被调用时,str1 = “abc” 已经将“abc”放入了常量池,根据方法注释,str3返回的是拿到的是常量池中的str1,str2还是对象引用。所以有了上面的结果。
示例二:
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2); //false
System.out.println(str1.intern() == str2.intern()); //true
}
运行结果:
false
true
分析,str1==str2为false就不用说了;str1.intern() == str2.intern()为true,在英文注释里有说:
s.intern() == t.intern() is true if and only if s.equals(t) is true.
也是就是两个String对象,当且仅当它俩equals()比较值为true,那它俩intern()的“==”操作也为true。
示例三:
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = str1.intern();
String str3 = "abc";
System.out.println(str1 == str3); //false
System.out.println(str3 == str2); //true
}
运行结果:
false
true
分析,new Sting() 会在堆内创建一个str1的String对象,“abc”会添加到常量池,在调用intern()方法时,会去常量池中查找是否有相等的字符串字面量和对象引用,此时有“abc”就返回了“abc”,所以str3 == str2为true。str1 == str3为false就不用说了。
示例四:
类成员变量也会add到常量池。
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.setName("King");
String name = teacher.getName();
String str = "King";
System.out.println(name == str); // true
}
static class Teacher {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
运行结果:
true
分析,类的成员变量在Class常量池中,英文文档上没有说明,但代码示例验证后,说明类成员变量也会放到常量池。
示例五:
public static void main(String[] args) {
String s1 = new String("abc") + new String("abc");
String s2 = s1.intern();
String s3 = "abc" + "abc";
System.out.println(s1 == s2); //true
System.out.println(s2 == s3); //true
System.out.println(s1 == s3); //true
}
运行结果:
true
true
true
分析,“new String(“abc”) + new String(“abc”)”首先会创建这两个对象以及相加后的对象,然后在常量池放入“abc”且对象指向该字面量。当s1调用intern()时因为没有“abcabc”的字面量,然后在常量池添加了s1的对象引用,返回给s2。所以,s1 == s2为true。““abc” + “abc””双引号相加,会判断这两个常量、相加后的常量在常量池上是否存在,
如果不存在
则在常量池上创建相应的常量
如果存在
判断这个常量是存在的引用还是常量,
如果是引用,返回引用地址指向的堆空间对象,
如果是常量,则直接返回常量池常量
所以,““abc” + “abc””等同于“new String(“abc”) + new String(“abc”)”,s2 == s3与s1 == s3都为true。
花了不少时间分析、总结出来的,希望能帮助阅读者更快地理解它。
至此,本篇结束。
本文地址:https://blog.csdn.net/WLQ0621/article/details/107465898