intern
9 static void test01(){
10 String s1 = new String("1")+new String("23");
11 s1.intern();
12 String s2 = "123";
13 System.out.println( s1 == s2);//true
14 }
15
16 static void test02(){
17 String s1 = new String("1")+new String("23");
18 String s2 = "123";
19 s1.intern();
20 System.out.println( s1 == s2); //false
21 }
s1.intern 在常量池植入引用,指向堆里面的"123",s2来的时候先看常量区,已经有了(虽然只是一个引用),直接给变量赋值
23 static void test03(){
24 String s1 = new String("1")+new String("23");
25 String s2 = "123";
26 System.out.println( s1 == s2);//false
27 s1.intern();
28 System.out.println( s1 == s2);//false
29 s1 = s1.intern();
30 System.out.println( s1 == s2);//true
31 }
s1.intern试图在常量池植入"123",一看已经有了,返回常量池 “123”的引用,什么都不干,27行返回值没用所以28行还是false,29行用了给s1赋值,然后s1和s2就指向同一片了
如果常量池没有,那么会将堆中的字符串的引用放到常量池,注意是引用,然后返回该引用。
既然常量池已经在Java6之后放入了堆中,那么如果堆中已经创建过此字符串的对象了,那么就没有必要在常量池中再创建一个一毛一样的对象了,直接将其引用拷贝返回就好了,因为都是处于同一个区域Java堆中。
26 /**
27 * 第一行,StringBuilder只会在堆中创建一个对象,第二行调用intern方法后,会将堆中的引用放到到常量池中。
28 * 第三行发现常量池中已经有这个字符串的引用了,直接返回。
29 * 因此是同一个引用,返回的都是第一次创建的堆中字串的引用
30 */
31 void test03() {
32 StringBuilder s1 = new StringBuilder("xyz");
33 String s2 = s1.toString().intern();
34 String s3 = "xyz";
35 System.out.println(s2 == s3); // true
36 }
这个例子不能证明现在"xyz"真身是在常量池还是堆
48 /**
49 * new String之后使用 + 在Java中会进行编译优化,编译成字节码指令后,会将 + 优化成 先new Stringbuilder对象,然后调用append方法进行拼接。
50 * 因此这里s1最终创建的时候,xyzz字符串并没有在常量池创建,只是在堆中创建了,因为就如同上面的test03()一样,是new Stringbuilder操作。
51 * 所以在调用intern操作后,将其堆中的引用放入常量池并返回。
52 * 所以后面的结果都是true,因为至始至终都是堆中的一个对象。
53 */
54 void test05() {
55 String s1 = new String("xyz") + new String("z");
56 String s2 = s1.intern();
57 String s3 = "xyzz";
58 System.out.println(s1 == s2); // true
59 System.out.println(s1 == s3); // true
60 System.out.println(s2 == s3); // true
61 }
这个例子证明了"xyzz"真身在堆,因为s1一定在堆
56行,在常量池创建堆中真身的引用
72 /**
73 * s1指向的对象并没有改变
74 * s2指向常量区,s1指向堆,所以不一样
75 */
76 void test07() {
77 String s1 = new String("xyz") + new String("z");
78 String s2 = "xyzz";
79 s1.intern();
80 System.out.println(s1 == s2); // false
81 }
这个例子跟上一个例子key区别在于s2在定义的的时候,常量池还没有s1的引用。反证
83 /**
84 * s1.intern()之后,在常量区添加了堆中"xyzz"的引用,s2指向了这个常量池中"xyzz"对象
85 * 因此二者不相等
86 */
87 void test08() {
88 String s1 = new String("xyz") + new String("z");
89 s1.intern();
90 String s2 = "xyzz";
91 System.out.println(s1 == s2); // false
92 }
94 /**
95 * 第一个判断,
96 * s1.intern()之后,在常量区添加了堆中"xyzz"的引用
97 * s2也指向了常量池中这个引用,但是s1本身没有变,指的是堆中对象的引用,因此不相等
98 * <p>
99 * 第二个判断,
100 * s1 = s1.intern()以后,s1也指向了常量池中这个引用,因此相等
101 */
102 void test09() {
103 String s1 = new String("xyz") + new String("z");
104 s1.intern();
105 String s2 = "xyzz";
106 System.out.println(s1 == s2); // false
107 s1 = s1.intern();
108 System.out.println(s1 == s2); // true
109 }
1 public class Test {
2 public static void main(String[] args) {
3 String str1 = new StringBuilder("计算机").append("软件").toString();
4 String str2 = str1.intern();
5 String str3 = new StringBuilder("ja").append("va").toString();
6 String str4 = str3.intern();
7 System.out.println(str1==str2);
8 System.out.println(str3==str4);
9 }
10 }
jdk1.8的输出答案是true和false。
jdk1.6的输出是两个false。
jdk1.7的intern方法不会在复制实例,只是在常量池中记录首次出现的实例引用。
因此str2指向的引用其实就是str1指向Java堆中StringBuilder创建的字符串实例。所以返回结果为true
但是java这个字符串常量在编译期就已经在方法区的常量池中了,不符合首次出现,所以str4指向的是常量池中的java字面量
所以返回结果为false。
https://www.cnblogs.com/r1-12king/p/15946648.html
26 /** 27 * 第一行,StringBuilder只会在堆中创建一个对象,第二行调用intern方法后,会将堆中的引用放到到常量池中。 28 * 第三行发现常量池中已经有这个字符串的引用了,直接返回。 29 * 因此是同一个引用,返回的都是第一次创建的堆中字串的引用 30 */ 31 void test03() { 32 StringBuilder s1 = new StringBuilder("xyz"); 33 String s2 = s1.toString().intern(); 34 String s3 = "xyz"; 35 System.out.println(s2 == s3); // true 36 }
实践:1.8
// String row1 = ("xxxxx");
// String row2 = new String("xxxxx");
// row2 = row2.intern();
// System.out.println(row1 == row2); //true
// String row1 = new String("xxxxx");
// String row2 = new String("xxxxx");
// row2 = row2.intern();
// System.out.println(row1 == row2); //false
// String row1 = new String("xxxxx");
// String row2 = new String("xxxxx");
// System.out.println(row1 == row2); // false
// row2 = row2.intern();
// row1 = row1.intern();
// System.out.println(row1 == row2); // true
intern的缺点:
这个功能为String提供了字符串池,我们可以使用它来优化内存。 但是,这有一个缺点:在OpenJDK中,String.intern()是本地方法,它实际上调用了JVM的相关方法来实现该功能。这样实现的原因是,当VM和JDK代码必须就特定String对象的标识达成一致时,String interning就必须是JDK-VM接口的一部分。
这样的实现意味着:
- 您需要在每个intern调用使用JDK-JVM接口,这会浪费CPU。
- 性能受本地HashTable实现的影响,可能落后于高性能Java版本,特别是在并发访问的情况下。
- 由于Java Strings是来自VM的引用,因此它们成为GC rootset的一部分。 在许多情况下,这需要在GC停顿期间执行额外的工作。
https://www.cnblogs.com/helloworld2048/p/java_string_intern.html
浙公网安备 33010602011771号