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接口的一部分。

这样的实现意味着:

  1. 您需要在每个intern调用使用JDK-JVM接口,这会浪费CPU。
  2. 性能受本地HashTable实现的影响,可能落后于高性能Java版本,特别是在并发访问的情况下。
  3. 由于Java Strings是来自VM的引用,因此它们成为GC rootset的一部分。 在许多情况下,这需要在GC停顿期间执行额外的工作。

https://www.cnblogs.com/helloworld2048/p/java_string_intern.html

posted on 2024-05-10 14:34  silyvin  阅读(1)  评论(0编辑  收藏  举报