精华内容
下载资源
问答
  • 关于String.intern()方法,这个问题都被问烂了,有的文章在分析的时候还在用jdk1.7,jdk1.8之后内存模型发生了变化,内存的变化也会影响intern方法的执行,这里有必要写文章分析一下,请大家务必从头开始看,这样...
  • 字符串在 Python 中是最简单也是最常用的数据类型之一,在 CPython 中字符串的实现原理使用了一种叫做 Intern(字符串驻留)的技术来提高字符串效率。究竟什么是 intern 机制,这种机制又是通过什么方式来提高字符串...
  • intern机制:  字符串类型作为Python中最常用的数据类型之一,Python解释器为了提高字符串使用的效率和使用性能,做了很多优化,例如:Python解释器中使用了 intern(字符串驻留)的技术来提高字符串效率,什么是...
  • intern-源码

    2021-03-11 14:38:24
    Nakuja实习生2021
  • 主要给大家介绍了关于C#中字符串优化String.Intern、IsInterned的相关资料,文中通过示例代码介绍的,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
  • intern-app-源码

    2021-03-06 01:57:58
    intern-app
  • 从你的浏览器中创建功能测试,用于观察茉莉或实习生。 该测试记录器使您可以轻松创建功能测试。 不必担心要用于测试的框架,只需担心要创建的测试。 在一些最重要的框架(如Jasmine,TheIntern或Nightwatch *)中...
  • Intern_project-源码

    2021-03-15 23:12:18
    Create React App入门 该项目是通过引导的。 可用脚本 在项目目录中,可以运行: npm start 在开发模式下运行应用程序。 打开在浏览器中查看它。 如果您进行编辑,则页面将重新加载。 您还将在控制台中看到任何...
  • 复制权限:已授予 这是我的Mozilla Intern 2015演示文稿的幻灯片。 如果您想观看伴随演讲的幻灯片,可以在air mozilla上找到演讲: : 注意:使用reveal.js-复制/剪切代码需要使用firefox 41或更高版本。
  • 主要介绍了Java中==运算符与equals方法的区别及intern方法详解的相关资料,需要的朋友可以参考下
  • Create React App入门 该项目是通过引导的。 可用脚本 在项目目录中,可以运行: yarn start 在开发模式下运行应用程序。 打开在浏览器中查看它。 如果您进行编辑,则页面将重新加载。 您还将在控制台中看到任何...
  • JavaScript 自动化测试框架 Intern ,Intern 是一个 JavaScript自动化测试框架,支持浏览器和nodejs两种环境,支持AM...
  • 想要理解intern()方法必须先了解String创建对象的方式,对String及字符串常量池有深入了解的小伙伴可以直接跳到第二部分进行阅读,不了解String的小伙伴就先补补课吧。 一、深入理解String创建对象 String str = ...

    想要理解intern()方法必须先了解String创建对象的方式,对String及字符串常量池有深入了解的小伙伴可以直接跳到第二部分进行阅读,不了解String的小伙伴就先补补课吧。

    一、深入理解String创建对象

    String str = "计算机";

    这行代码会直接在字符串常量池中创建一个字符串对象,然后将栈中的str变量指向它。

    String str = "计算机";
    String str2 = "计算机";

    如果我们再创建一个“str2”,其值也是“计算机”的话就会直接将栈中的“str2”变量也指向字符串常量池中已存在的“计算机”对象,从而避免重复创建对象,字符串常量池存在的原因也是如此。除此之外,常量池中还会存在很多java关键字,避免每次出现都重新创建,比如“java”这个关键字无论你是否创建它都会一直存在于字符串常量池中。

    此时如下代码一定会返回true,因为“str”和“str2”指向同一个地址的同一个对象。

    System.out.println(str == str2); // true

     

    上面说的两种方式都是直接创建字符串,如果通过new关键字创建字符串对象情况就会有很大不同。

    String str = new String("计算机");

    当代码执行到括号中的"计算机"的时候会检测常量池中是否存在“计算机”这个对象,如果不存在则在字符串常量池中创建一个对象。当整行代码执行完毕时会因为new关键字在堆中创建一个“计算机”对象,并把栈中的变量“str”指向堆中的对象,如下图所示。这也是为什么说通过new关键字在大部分情况下会创建出两个字符串对象,推荐直接创建字符串对象而不用new。

     

    String str = new String("计算机");
    String str2 = new String("计算机");

    同理,如果我们此时再创建一个值也是“计算机”的“str2”对象,当执行到第二行括号中的“计算机”时首先检测常量池中是否有“计算机”这个对象,因为第一行代码已经将其创建,所以有的话就不创建了;当第二行代码执行完毕会因为new关键字在堆中创建出一个新的对象,并将栈中的变量str2指向该对象。

     

    此时如下代码一定会返回false,因为“str”和“str2”指向不同地址的两个对象。

    System.out.println(str == str2); // false

    二、深入理解intern()方法

    jdk1.8中是这样描述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&trade; 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();

    翻译过来就是,当intern()方法被调用的时候,如果字符串常量池中已经存在这个字符串对象了,就返回常量池中该字符串对象的地址;如果字符串常量池中不存在,就在常量池中创建一个指向该对象堆中实例的引用,并返回这个引用地址(jdk1.7之前会直接将对象赋值到常量池中)。

    听起来比较拗口,下面我们举几个例子来解释一下。

    示例一:

    周志明老师在《深入理解Java虚拟机》一书中举例如下

    String str1 = new StringBuilder("计算机").append("软件").toString();
    System.out.println(str1.intern()==str1); // true

    第一行代码

    • 执行到括号中的“计算机”时会在字符串常量池中添加“计算机”对象;
    • 执行到括号中的“软件”时会在字符串常量池中添加“软件”对象;
    • 执行完毕后会在堆中创建“计算机软件”对象,并将栈中的str1变量指向该对象;

    第二行代码

    • 执行到str1.intern()的时候发现字符串常量池中没有“计算机软件”这个字符串,于是在字符串常量池中创建一个引用,指向堆中的对象(即指向和str1相同),然后返回该引用;
    • 执行完毕后打印true;

    内存中的图示如下,由于str1和str1.intern()都指向堆中的对象,因此结果为true。

     

    String str2 = new StringBuilder("ja").append("va").toString();
    System.out.println(str2.intern() == str2); // false

    第一行代码

    • 执行到“ja”的时候在字符串常量池中创建“ja”对象;
    • 执行到“va”的时候在字符串常量池中创建“va”对象;
    • 执行完毕后会在堆中创建“java对象”,并将栈中的变量str2指向该对象;

    第二行代码

    • 执行到str2.intern()的时候发现字符串常量池中已经存在“java”对象,因此返回该对象的引用(这个java对象并不是我们创建的,因为“java”是java语言中的关键字,它是字符串常量池中默认就存在的);
    • 执行完毕后打印false;

    内存中的图示如下,因为str2.intern()指向字符串常量池中的对象,而str2指向堆中的对象,因此结果为false。

    下面会罗列大量的例子来解释这个intern()函数,小伙伴们看到哪个例子感觉自己又行了,都掌握了,就可以停止了。

    示例二:

    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2); // false
    

    第一行代码

    • 执行到括号中的“1”的时候会在字符串常量池中创建“1”对象;
    • 执行完毕后会在堆中创建“1”对象,并将栈中的s变量指向该对象;

    第二行代码执行完毕后会因为“1”已经在字符串常量池中了,而不做其他操作,只会把这个“1”的地址返回,但是我们并没有接收;

    第三行代码执行完毕时会因为字符串常量池中已经有“1”这个对象了,而不执行创建操作,直接将栈中的变量s2指向这个对象;

    第四行代码执行完毕后打印false;

    内存中的图示如下,因为s指向堆中的对象,而s2指向字符串常量池中的对象,因此结果为false。

    String s3 = new String("1") + new String("1");
    s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4); // true

    第一行代码

    • 执行到括号中的“1”时会在字符串常量池中创建一个对象“1”,执行到第二个括号中的“1”时因为池中已经有“1”这个对象了,因此不重复创建;
    • 执行完毕后在堆中创建对象“11”,并将栈中的变量s3指向该对象;

    第二行代码执行完毕后会因为字符串常量池中没有“11”这个对象,而在字符串常量池中创建一个引用,指向队中的“11”这个对象,也就是和s3的指向相同;

    第三行代码执行完毕后发现字符串常量值中已经有“11”了,只不过它是一个指向堆中的引用(这里可以理解为堆中已经有“11”这个对象了),那么不重复创建,而是直接将s4指向该引用。

    第四行代码执行完毕后打印true

    内存中的图示如下,因为s3和s4同时指向堆中的对象,因此结果为true

    String s3 = new String("1") + new String("1");
    String s4 = "11";
    s3.intern();
    System.out.println(s3 == s4); // false

    第一行代码

    • 执行到括号中的“1”时会在字符串常量池中创建对象“1”,执行到第二个括号中的“1”时因为该对象已经存在于字符串常量池中因此不再创建;
    • 执行完毕后在堆中创建对象“11”,并将栈中的变量s3指向该对象;

    第二行代码执行完毕后发现常量池中没有“11”这个对象,因此在字符串常量池中创建这个对象,并将栈中的变量s4指向该对象;

    第三行代码执行完毕后发现字符串常量池中已经有“11”这个对象了,因此返回该对象地址,但我们并没有接收;

    第四行代码执行完毕后打印false;

    内存中的图示如下,因为s3指向堆中的对象,而s4指向字符串常量池中的对象,因此结果为false。

     

    示例三

    String str1 = new String("SEU") + new String("Calvin");
    System.out.println(str1.intern() == str1); // true
    System.out.println(str1 == "SEUCalvin"); // true

    第一行代码

    • 执行到第一个括号中的“SEU”时在字符串常量池中创建“SEU”对象,执行到第二个括号中的“Calvin”时在字符串常量池中创建“Calvin”对象;
    • 执行完毕后在堆中创建“SEUCalvin”对象,并将栈中的变量str1指向该对象;

    第二行代码

    • 执行到str1.intern()时因为字符串常量池中没有“SEUCalvin”对象,而在常量池中创建一个引用,该引用指向堆中的“SEUCalvin”对象,也就是该引用和str1的指向相同;
    • 执行完毕后打印true;

    第三行代码执行完毕后打印true;

    内存中的图示如下,因为str1.intern()和str1同时指向堆中的同一个对象,因此第二行结果为true;因为字符串常量池中的“SEUCalvin”对象是一个指向队中“SEUCalvin”对象的引用,而str1也指向堆中的这个对象,因此第三行结果为true。

    其实第二行和第三行代码比较的都是两个绿色部分是否相等。

    String str2 = "SEUCalvin"; // 新加的一行代码,其余不变
    String str1 = new String("SEU") + new String("Calvin");
    System.out.println(str1.intern() == str1); // false
    System.out.println(str1 == "SEUCalvin"); // false

    这几行代码只是在最前面加了一行,其他三行完全是上面的代码,但结果却截然不同。

    第一行代码会在字符串常量池中创建“SEUCalvin”对象,并将栈中的变量str2指向该对象;

    第二行代码

    • 执行到括号中的“SEU”时会在字符串常量池中创建“SEU”对象,执行到括号中的“Calvin”时会在字符串常量池中创建“Calvin”对象;
    • 执行完毕后会在堆中创建“SEUCalvin”对象,并将栈中的str1变量指向该对象;

    第三行代码

    • 执行到str1.intern()时发现字符串常量池中已经存在“SEUCalvin”对象,因此直接返回池中该对象地址;
    • 执行完毕后打印false;

    第四行代码

    • 执行到括号中的“SEUCalvin”会因字符串常量池中已经存在该对象而不做任何操作;
    • 执行完毕打印false;

    内存中的图示如下,因为str1.intern()指向字符串常量池中的对象,而str1指向堆中的对象,因此结果为false。

    补充:

    String str1 = new String("SEU") + new String("Calvin");

    此行代码会创建5个对象。分别是:

    • 常量池中的“SEU”对象
    • 常量池中的“Calvin”对象
    • 堆中的“SEU”对象,直接变成垃圾
    • 堆中的“Calvin”对象,直接变成垃圾
    • 堆中的“SEUClvin”对象,栈中的str1变量指向该对象

    由于堆中的“SEU”对象和“Calvin”对象直接变成垃圾了,因此上文中并没有分析这两个对象。

     

    本文结论为总结官方文档、个人理解及代码实战得出,如有错误,欢迎指正。

    展开全文
  • 目录1.`intern`方法解释2.`new String()`到底创建了几个对象3.`intern()`面试难题4.`intern()`总结5.`intern()`练习题6.`intern()`空间效率 1.intern方法解释 1.任意的字符串调用String.intern()方法,都会先去字符...

    1.intern方法解释

    1.任意的字符串调用String.intern()方法,都会先去字符串常量池中寻找有没有相同的字符串,如果找到,则直接返回字符串常量池中的引用。
    如果没有找到,JDK6JDK7及以后的处理存在区别:
    a)JDK6会直接复制一个String对象的副本放到常量池
    b)JDK7及以后则是生成一个对原字符串对象的引用放在字符串常量池中。
    在这里插入图片描述

    2.new String()到底创建了几个对象

    下面的Java代码创建了几个对象:

    package jvm;
    /**
     * 题目:
     * new String("ab")会创建几个对象?看字节码,就知道是两个。
     *     一个对象是:new关键字在堆空间创建的
     *     另一个对象是:字符串常量池中的对象"ab"。 字节码指令:ldc,是从常量池中获取值
     *
     *
     * 思考:
     * new String("a") + new String("b")呢?
     *  对象1:new StringBuilder()
     *  对象2: new String("a")
     *  对象3: 常量池中的"a"
     *  对象4: new String("b")
     *  对象5: 常量池中的"b"
     *
     *  深入剖析: StringBuilder的toString():
     *      对象6 :new String("ab")
     *       强调一下,toString()的调用,在字符串常量池中,没有生成"ab"
     * 
     */
    public class StringNewTest {
        public static void main(String[] args) {
        	String str = new String("ab");
    //      String str = new String("a") + new String("b");
        }
    }
    

    字节码分析:
    1.注释掉// String str = new String("a") + new String("b");,使用javap -v -p StringNewTest反编译上面的java代码产生的class文件,得到下面的字节码输出:
    可以看到,总共创建了两个对象,一个是new关键字在堆空间中创建出来的对象(0: new #16 // class java/lang/String);另一个是字符串常量池中的对象(4: ldc #18 // String ab)。
    值得注意的是:如果单独问String str = new String("ab");这句代码创建了几个对象,答案不一定是2个,也有可能是1个,因为字符串常量池中如果已经存在了字符串"ab",是不会再创建的。这里是main函数的第一行代码,显然常量池中是不存在"ab"的,所以会创建。

        Code:
          stack=3, locals=2, args_size=1
             0: new           #16                 // class java/lang/String
             3: dup
             4: ldc           #18                 // String ab
             6: invokespecial #20                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
             9: astore_1
            10: return
    

    2.注释掉String str = new String("ab");,使用javap -v -p StringNewTest反编译上面的java代码产生的class文件,得到下面的字节码输出:

        Code:
          stack=5, locals=2, args_size=1
             0: new           #16                 // class java/lang/StringBuilder
             3: dup
             4: new           #18                 // class java/lang/String
             7: dup
             8: ldc           #20                 // String a
            10: invokespecial #22                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
            13: invokestatic  #25                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
            16: invokespecial #29                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
            19: new           #18                 // class java/lang/String
            22: dup
            23: ldc           #30                 // String b
            25: invokespecial #22                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
            28: invokevirtual #32                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            31: invokevirtual #36                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            34: astore_1
            35: return
    

    可以看到总共创建了6个对象:

     *  对象1:new StringBuilder() // 0: new     #16     // class java/lang/StringBuilder
     *  对象2: new String("a")   // 4: new    #18     // class java/lang/String
     *  对象3: 常量池中的"a"       // 8: ldc     #20    // String a
     *  对象4: new String("b")   // 19: new   #18     // class java/lang/String
     *  对象5: 常量池中的"b"       // 23: ldc  #30     // String b
     *
     *  深入剖析: StringBuilder的toString():  最后调用StringBuilder的toString方法,生成
     * 新的字符串返回。toString方法底层使用的是new String的方式创建的新的字符串。 
     *  对象6 :new String("ab")  // 31: invokevirtual #36   // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     *  强调一下,toString()的调用,在字符串常量池中,没有生成字符串,因为字节码文件中没有看见ldc
    

    3.intern()面试难题

    1.如何保证变量String s指向的是字符串常量池中的数据呢?
    有两种方式:

    • 方式一: String s = "shkstart";//字面量定义的方式
    • 方式二: 调用intern()方法
      1)String s = new String("shkstart").intern();
      2)String s = new StringBuilder("shkstart").toString().intern();

    2.JDK6JDK7及以上下面代码的输出是什么?

    package jvm;
    public class StringIntern {
        public static void main(String[] args) {
     
            String s = new String("1");// 调用new,总共创建了两个对象,一个是通过new在堆空间中创建的对象;另一个是在字符串常量池中创建的对象。s指向的是堆空间中的对象
            s.intern();//调用此方法之前,字符串常量池中已经存在了"1"
            String s2 = "1";// 由于字符串常量池中已经存在了"1",所以s2直接指向了字符串常量池中的"1"
            System.out.println(s == s2);//jdk6:false   jdk7/8:false 原因:s指向的是堆空间中的对象,s2指向的是字符串常量池中的对象,显然不相等
            
     
            String s3 = new String("1") + new String("1");//s3变量记录的地址为堆空间中的对象"11", 因为最终调用的StringBuilder的toString方法的底层调用的是new String("11")
            //执行完上一行代码以后,字符串常量池中,是否存在"11"呢?答案:不存在!!(toString方法不在常量池中创建对象)
            s3.intern();//字符串常量池中不存在11,所以intern方法会在字符串常量池中生成"11"。
            /*JDK6与JDK7及以上的intern()在字符串常量池中生成11的区别:*/
            //jdk6:在字符串常量池中创建了一个新的对象"11",也就有新的地址。
            //jdk7及以上:此时常量池中并没有创建"11",而是创建一个指向堆空间中new String("11")的地址,也就是s3指向的堆空间中的对象
            String s4 = "11";//会先去常量池中找。s4变量记录的地址:使用的是上一行代码代码执行时,在常量池中生成的"11"的地址
            System.out.println(s3 == s4);//jdk6:s3指向的是堆空间中new出来的对象,s4指向的是字符串常量池中创建的字符串的地址,所以结果为false  jdk7/8:s3指向的还是堆空间中new出来的对象,s4的值是堆空间中new出来的是对象"11"的地址,所以为true
        }
    }
    

    上面代码的图解:
    JDK6:字符串常量池在永久代。调用s3.intern()之后,会在字符串常量池生成一个字符串对象的拷贝。
    在这里插入图片描述

    JDK7及以上:字符串常量池在堆区。调用s3.intern()之后,会在字符串常量池生成一个对于堆区字符串对象的引用。
    在这里插入图片描述
    3.变型题
    JDK7及以上的环境下,下列代码的输出为?

    public class StringIntern1 {
        public static void main(String[] args) {
            //StringIntern.java中练习的拓展:
            String s3 = new String("1") + new String("1");//new String("11")
            //执行完上一行代码以后,字符串常量池中,是否存在"11"呢?答案:不存在!!
            String s4 = "11";//在字符串常量池中生成对象"11"
            String s5 = s3.intern();// 会将s3指向的对象的地址放到常量池中
            System.out.println(s3 == s4);//false,因为s3指向的是堆空间中的对象,s4指向的是字符串常量池中的对象
            System.out.println(s5 == s4);//true,s5最终也是指向的堆空间中的对象
        }
    }
    

    4.intern()总结

    1.在JDK6中,调用Stringintern()方法,如果字符串常量池中存在改字符串,则直接返回已有字符串的地址;如果没有,会把该字符串对象复制一份放到字符串常量池中,并返回对象地址。
    2.在JDK7及以后中,调用Stringintern()方法,如果字符串常量池中存在改字符串,则直接返回已有字符串的地址;如果没有,会把该字符串对象的引用地址复制一份放到字符串常量池中,并返回字符串常量池中的该引用地址。

    在这里插入图片描述

    5.intern()练习题

    题1:下面代码在JDK6JDK7及以上的输出为?

    public class StringExer1 {
        public static void main(String[] args) {
            //String x = "ab";
            String s = new String("a") + new String("b");//new String("ab")
            //在上一行代码执行完以后,字符串常量池中并没有"ab"
     
            String s2 = s.intern();//jdk6中:在串池中创建一个字符串"ab"
                                   //jdk8中:串池中没有创建字符串"ab",而是创建一个引用,指向new String("ab"),将此引用返回
     
            System.out.println(s2 == "ab");//jdk6:true  jdk8:true // 需要注意的是这里的"ab"并不会再去常量池中重新创建一个字符串变量。因为在常量池中"ab"已经存在(JDK6中复制的对象,是JDK7及以上,创建的堆空间中"ab"对象的引用),为了节约空间,不会重新创建
            System.out.println(s == "ab");//jdk6:false  jdk8:true
        }
    }
    

    上面代码的图解:
    JDK6中:
    在这里插入图片描述

    JDK7及以上:
    在这里插入图片描述
    如果打开//String x = "ab";的注释:
    在这里插入图片描述
    题2:下面代码的输出为?

    public class StringExer2 {
        public static void main(String[] args) {
            String s1 = new String("ab");//执行完以后,会在字符串常量池中会生成"ab"
    //        String s1 = new String("a") + new String("b");执行完以后,不会在字符串常量池中会生成"ab"
            s1.intern();
            String s2 = "ab";
            System.out.println(s1 == s2); // false, s1指向的是new出来的对象;s2指向的是常量池中的对象
        }
    }
    

    6.intern()空间效率

    对于程序中大量存在存在的字符串,尤其其中存在很多重复字符串时,使用intern()可以节省内存空间。

    更多JVM文章请访问我的JVM专栏:
    https://blog.csdn.net/u011069294/category_10113093.html

    展开全文
  • 0.引言 什么都先不说,先看下面这个引入的例子: [java]view plaincopy Stringstr1=newString("SEU")+newString("Calvin... System.out.println(str1.intern()==str1); System.out.println(str1=="SEUCalvin");...

    0.引言

    什么都先不说,先看下面这个引入的例子:

    [java] view plain copy

    1. String str1 = new String("SEU")+ new String("Calvin");      
    2. System.out.println(str1.intern() == str1);   
    3. System.out.println(str1 == "SEUCalvin");  

     

    本人JDK版本1.8,输出结果为:

     

    [java] view plain copy

    1. true  
    2. true  

    再将上面的例子加上一行代码:

    [java] view plain copy

    1. String str2 = "SEUCalvin";//新加的一行代码,其余不变  
    2. String str1 = new String("SEU")+ new String("Calvin");      
    3. System.out.println(str1.intern() == str1);   
    4. System.out.println(str1 == "SEUCalvin");   

    再运行,结果为:

     

     

    [java] view plain copy

    1. false  
    2. false  

    是不是感觉莫名其妙,新定义的str2好像和str1没有半毛钱的关系,怎么会影响到有关str1的输出结果呢?其实这都是intern()方法搞的鬼!看完这篇文章,你就会明白。o(∩_∩)o 

    说实话我本来想总结一篇Android内存泄漏的文章的,查阅了很多资料,发现不得不从Java的OOM讲起,讲Java的OOM又不得不讲Java的虚拟机架构。如果不了解JVM的同学可以查看此篇 JVM——Java虚拟机架构。(这篇文章已经被我修改过N多次了,个人感觉还是挺全面清晰的,每次看都会有新的理解。)

    在JVM架构一文中也有介绍,在JVM运行时数据区中的方法区有一个常量池,但是发现在JDK1.6以后常量池被放置在了堆空间,因此常量池位置的不同影响到了String的intern()方法的表现。深入了解后发现还是值得写下来记录一下的。

     

     

    1.为什么要介绍intern()方法

    intern()方法设计的初衷,就是重用String对象,以节省内存消耗。这么说可能有点抽象,那么就用例子来证明。

     

    [java] view plain copy

    1. static final int MAX = 100000;  
    2. static final String[] arr = new String[MAX];  
    3.   
    4. public static void main(String[] args) throws Exception {  
    5.     //为长度为10的Integer数组随机赋值  
    6.     Integer[] sample = new Integer[10];  
    7.     Random random = new Random(1000);  
    8.     for (int i = 0; i < sample.length; i++) {  
    9.         sample[i] = random.nextInt();  
    10.     }  
    11.     //记录程序开始时间  
    12.     long t = System.currentTimeMillis();  
    13.     //使用/不使用intern方法为10万个String赋值,值来自于Integer数组的10个数  
    14.         for (int i = 0; i < MAX; i++) {  
    15.             arr[i] = new String(String.valueOf(sample[i % sample.length]));  
    16.             //arr[i] = new String(String.valueOf(sample[i % sample.length])).intern();  
    17.         }  
    18.         System.out.println((System.currentTimeMillis() - t) + "ms");  
    19.         System.gc();  
    20. }  

     

    这个例子也比较简单,就是为了证明使用intern()比不使用intern()消耗的内存更少。

    先定义一个长度为10的Integer数组,并随机为其赋值,在通过for循环为长度为10万的String对象依次赋值,这些值都来自于Integer数组。两种情况分别运行,可通过Window ---> Preferences --> Java --> Installed JREs设置JVM启动参数为-agentlib:hprof=heap=dump,format=b,将程序运行完后的hprof置于工程目录下。再通过MAT插件查看该hprof文件。
    两次实验结果如下:

     

    从运行结果来看,不使用intern()的情况下,程序生成了101762个String对象,而使用了intern()方法时,程序仅生成了1772个String对象。自然也证明了intern()节省内存的结论。

    细心的同学会发现使用了intern()方法后程序运行时间有所增加。这是因为程序中每次都是用了new String后又进行intern()操作的耗时时间,但是不使用intern()占用内存空间导致GC的时间是要远远大于这点时间的。 

     

     

    2.深入认识intern()方法

    JDK1.7后,常量池被放入到堆空间中,这导致intern()函数的功能不同,具体怎么个不同法,且看看下面代码,这个例子是网上流传较广的一个例子,分析图也是直接粘贴过来的,这里我会用自己的理解去解释这个例子:

    [java] view plain copy

    1. String s = new String("1");  
    2. s.intern();  
    3. String s2 = "1";  
    4. System.out.println(s == s2);  
    5.   
    6. String s3 = new String("1") + new String("1");  
    7. s3.intern();  
    8. String s4 = "11";  
    9. System.out.println(s3 == s4);  

    输出结果为:

    [java] view plain copy

    1. JDK1.6以及以下:false false  
    2. JDK1.7以及以上:false true  

    再分别调整上面代码2.3行、7.8行的顺序:

    [java] view plain copy

    1. String s = new String("1");  
    2. String s2 = "1";  
    3. s.intern();  
    4. System.out.println(s == s2);  
    5.   
    6. String s3 = new String("1") + new String("1");  
    7. String s4 = "11";  
    8. s3.intern();  
    9. System.out.println(s3 == s4);  

    输出结果为:

     

    [java] view plain copy

    1. JDK1.6以及以下:false false  
    2. JDK1.7以及以上:false false  

     

    下面依据上面代码对intern()方法进行分析:

     

    2.1 JDK1.6

     

    在JDK1.6中所有的输出结果都是 false,因为JDK1.6以及以前版本中,常量池是放在 Perm 区(属于方法区)中的,熟悉JVM的话应该知道这是和堆区完全分开的。

    使用引号声明的字符串都是会直接在字符串常量池中生成的,而 new 出来的 String 对象是放在堆空间中的。所以两者的内存地址肯定是不相同的,即使调用了intern()方法也是不影响的。如果不清楚String类的“==”和equals()的区别可以查看我的这篇博文Java面试——从Java堆、栈角度比较equals和==的区别

    intern()方法在JDK1.6中的作用是:比如String s = new String("SEU_Calvin"),再调用s.intern(),此时返回值还是字符串"SEU_Calvin",表面上看起来好像这个方法没什么用处。但实际上,在JDK1.6中它做了个小动作:检查字符串池里是否存在"SEU_Calvin"这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会把"SEU_Calvin"添加到字符串池中,然后再返回它的引用。然而在JDK1.7中却不是这样的,后面会讨论。

     

    2.2 JDK1.7

     

    针对JDK1.7以及以上的版本,我们将上面两段代码分开讨论。先看第一段代码的情况:

     

    再把第一段代码贴一下便于查看:

    [java] view plain copy

    1. String s = new String("1");  
    2. s.intern();  
    3. String s2 = "1";  
    4. System.out.println(s == s2);  
    5.   
    6. String s3 = new String("1") + new String("1");  
    7. s3.intern();  
    8. String s4 = "11";  
    9. System.out.println(s3 == s4);  

     

    String s = newString("1"),生成了常量池中的“1” 和堆空间中的字符串对象。

    s.intern(),这一行的作用是s对象去常量池中寻找后发现"1"已经存在于常量池中了。

    String s2 = "1",这行代码是生成一个s2的引用指向常量池中的“1”对象。

    结果就是 s 和 s2 的引用地址明显不同。因此返回了false。

     

     

    String s3 = new String("1") + newString("1"),这行代码在字符串常量池中生成“1” ,并在堆空间中生成s3引用指向的对象(内容为"11")。注意此时常量池中是没有 “11”对象的。

    s3.intern(),这一行代码,是将 s3中的“11”字符串放入 String 常量池中,此时常量池中不存在“11”字符串,JDK1.6的做法是直接在常量池中生成一个 "11" 的对象。

    但是在JDK1.7中,常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用直接指向 s3 引用的对象,也就是说s3.intern() ==s3会返回true。

    String s4 = "11", 这一行代码会直接去常量池中创建,但是发现已经有这个对象了,此时也就是指向 s3 引用对象的一个引用。因此s3 == s4返回了true。

     

    下面继续分析第二段代码:

    再把第二段代码贴一下便于查看:

    [java] view plain copy

    1. String s = new String("1");  
    2. String s2 = "1";  
    3. s.intern();  
    4. System.out.println(s == s2);  
    5.   
    6. String s3 = new String("1") + new String("1");  
    7. String s4 = "11";  
    8. s3.intern();  
    9. System.out.println(s3 == s4);  

    String s = newString("1"),生成了常量池中的“1” 和堆空间中的字符串对象。

    String s2 = "1",这行代码是生成一个s2的引用指向常量池中的“1”对象,但是发现已经存在了,那么就直接指向了它。

    s.intern(),这一行在这里就没什么实际作用了。因为"1"已经存在了。

    结果就是 s 和 s2 的引用地址明显不同。因此返回了false。

     

     

    String s3 = new String("1") + newString("1"),这行代码在字符串常量池中生成“1” ,并在堆空间中生成s3引用指向的对象(内容为"11")。注意此时常量池中是没有 “11”对象的。

    String s4 = "11", 这一行代码会直接去生成常量池中的"11"。

    s3.intern(),这一行在这里就没什么实际作用了。因为"11"已经存在了。

    结果就是 s3 和 s4 的引用地址明显不同。因此返回了false。

    为了确保文章的实时更新,实时修改可能出错的地方,请确保这篇是原文,而不是无脑转载来的“原创文”,原文链接为:SEU_Calvin的博客

     

     

    3 总结

    终于要做Ending了。现在再来看一下开篇给的引入例子,是不是就很清晰了呢。

     

    [java] view plain copy

    1. String str1 = new String("SEU") + new String("Calvin");        
    2. System.out.println(str1.intern() == str1);     
    3. System.out.println(str1 == "SEUCalvin");    

     

    str1.intern() == str1就是上面例子中的情况,str1.intern()发现常量池中不存在“SEUCalvin”,因此指向了str1。 "SEUCalvin"在常量池中创建时,也就直接指向了str1了。两个都返回true就理所当然啦。

    那么第二段代码呢:

     

    [java] view plain copy

    1. String str2 = "SEUCalvin";//新加的一行代码,其余不变  
    2. String str1 = new String("SEU")+ new String("Calvin");      
    3. System.out.println(str1.intern() == str1);   
    4. System.out.println(str1 == "SEUCalvin");   

    也很简单啦,str2先在常量池中创建了“SEUCalvin”,那么str1.intern()当然就直接指向了str2,你可以去验证它们两个是返回的true。后面的"SEUCalvin"也一样指向str2。所以谁都不搭理在堆空间中的str1了,所以都返回了false。
     

     

    好了,本篇对intern的作用以及在JDK1.6和1.7中的实现原理的介绍就到此为止了。希望能给你带来帮助。

    转载请注明出处http://blog.csdn.net/seu_calvin/article/details/52291082

    展开全文
  • ) 拓展: 总结String的intern()的使用: intern练习1: intern练习2: 六、intern()效率测试 空间角度(jdk1.8) 七、StringTable的垃圾回收 八、G1中的String去重操作 实现 命令行选项 一、String的基本特性 1....

    目录

    一、String的基本特性

    为什么jdk9String由char[]改为了byte[]去存?

    String存储结构发生了变更,StringBuffer和StringBuilder是否改变了?

    实例(因为String不可变性,原来地址的值是无法被改变的):

    二、String的内存分配

    为什么要将StringTable进行调整,从永久代->堆?

    三、String的基本操作

    实例1:

    实例2:

    四、字符串拼接操作

    实例1:(常量与常量的拼接结果在常量池)

    实例2:(拼接其中有一个是变量,结果就在堆中,相当于新new的String)

    实例3:(字符串变量拼接底层是用StringBuilder)

    实例4:

    实例5:练习

    实例6:多个字符串拼接,其实也只new了一个StringBuilder

    实例7:for循环中拼接字符串

    总结:

     五、intern()的使用

    如何保证变量s指向字符串常量池中的数据?

    new String("abc")会创建几个对象?

    StringBuilder的toString()方法:

    经典题目(99%的人会做错!)

    拓展:

    总结String的intern()的使用:

    intern练习1:

    intern练习2:

    六、intern()效率测试

    空间角度(jdk1.8)

    七、StringTable的垃圾回收

    八、G1中的String去重操作

    实现

    命令行选项


    一、String的基本特性

    1.String字符串,使用一对""引起来表示。

    2.String声明为final的,不可被继承。

    3.String实现了Serializable接口,表示字符串是支持序列化的。实现了Comparable接口,表示String可以比较大小。

    4.String在jdk8及以前内部定义了final char[] value用于存储字符串数据。jdk9时改为byte[]

    5.String代表不可变的字符序列。简称:不可变性。

        (1)当字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。

        (2)当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

        (3)当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

    6.通过字面量的方式(区别于new,String a = 'abc';)给一个字符串赋值,此时字符串值声明在字符串常量池中。

    7.字符串常量池中是不会存储相同内容的字符串的

    8.String的String Pool是一个固定大小的HashTable,默认值大小长度是1009.如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降。

    9.使用-XX:StringTableSize可以设置StringTable的长度。

    10.在jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。StringTableSize设置没有要求。

    11.在jdk7中,StringTable的长度默认值是60013,StringTable的设置没有要求。

    12.jdk8中,StringTable的长度默认值是60013,1009是可设置的最小值。

    为什么jdk9String由char[]改为了byte[]去存?

        主要还是节省空间。

        JDK9 之前的库的 String 类的实现使用了 char 数组来存放字符串,char 占用16位,即两字节。

        研究发现String是堆空间的主要部分,同时大部分String都是一些拉丁文,一个字节就能够撑得下,如果使用char的话,因为char占两个字节,所以有一半的空间被浪费掉。

        所以,由char[]改为了byte[],再补一个字符标码集的标识coder;如果是ISO-8859-1、Latin-1就用一个byte去存,如果是UTF-16还是用两个字节去存。

        结论:String再也不用char[]来存储了,改成了byte[]加上编码标记,节约了一些空间。

    String存储结构发生了变更,StringBuffer和StringBuilder是否改变了?

    基于String的类,比如StringBuilder、StringBuffer以及HotSpot虚拟机内部的一些类也进行了修改。

    实例(因为String不可变性,原来地址的值是无法被改变的):

    二、String的内存分配

    1.在Java语言中有8种基本数据类型和一种比较特殊的类型String。这些类型为了使它们在运行过程中速度更快、更节省内存,都提供了一种常量池的概念。

    2.常量池就类似一个Java系统级别提供的缓存。8种基本数据类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种

        (1)直接使用双引号声明出来的String对象会直接存储在常量池中。(String a = "aaa";)

        (2)如果不是用双引号声明的String对象,可以使用String提供的intern()方法。

    3.Java 6及以前,字符串常量池存放在永久代。

    4.Java7中Oracle的工程师对字符串池的逻辑做了很大的改变,即将字符串常量池的位置调整到Java堆内

        所有的字符串都保存在堆(Heap)中,和其他普通对象一样,这样可以让你在进行调优应用时仅需要调整堆大小就可以了。

        字符串常量池概念原本使用得比较多,但是这个改动使得我们又足够的理由让我们重新考虑在java7中使用String.intern()。

    5.java8永久代变为元空间,字符串常量还是在堆。

     

    为什么要将StringTable进行调整,从永久代->堆?

    1.永久代空间有限,将字符串放在永久代容易报OOM-永久代内存溢出。

    2.永久代回收频率很低。

    三、String的基本操作

    java语言规范里要求完全相同的字符串字面量,应该包含同样的Unicode字符序列(包含同一份码点序列的常量),并且必须是指向同一个String类实例。

    实例1:

    debug下,可以逐行查看字符串常量池,首次加载1-10之后,再次打印就不会再往字符串常量池里加1-10字符串了:

     

    实例2:

    line3、line4的new的Object()、Memory()都是指向堆空间。

    line6的param也指向堆空间Object()。

    line7的param的toString()方法返回一个字符串,字符串存在字符串常量池里。

    四、字符串拼接操作

    1.常量与常量的拼接结果在常量池,原理是编译期优化。

    2.常量池中不会存在相同内容的常量。

    3.只要其中有一个是变量,结果就在堆中。变量拼接的原理是StringBuilder。

    4.如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。

    实例1:(常量与常量的拼接结果在常量池)

    反编译结果:

    实例2:(拼接其中有一个是变量,结果就在堆中,相当于新new的String)

    intern()方法就是判断字符串常量池中是否存在这个字符串,如果存在则返回常量池中字符串地址,如果不存在就在常量池中加载字符串并返回此地址。

    实例3:(字符串变量拼接底层是用StringBuilder)

    根据字节码指令可以看出,拼接字符串变量,先new了StringBuilder,然后使用append操作,然后调用toString()方法。

    StringBuilder的toString()方法,约等于new String("")。具体继续往下看。

    (补充:jdk1.5之后,拼接字符串使用StringBuilder,jdk1.5之前使用StringBuffer)

    实例4:

    字符串拼接操作,不一定是用的StringBuilder。如果拼接符号左右两边都是字符串常量或者常量引用(final修饰),则仍然使用编译期优化,非StringBuilder方式。

    所以,针对于final修饰的类、方法、基本数据类型、引用数据类型的结构时,能使用上final的时候建议使用上。

    反编译:

    实例5:练习

    实例6:多个字符串拼接,其实也只new了一个StringBuilder

    实例7:for循环中拼接字符串

    可以看出,for每循环一次就会new一个StringBuilder。所以,拼接字符串最好还是直接使用StringBuilder,而不是“+”。

     

    总结:

    字符串拼接最好使用StringBuilder调用append来拼接。

    使用加号“+”拼接,会new一个StringBuilder,并且在最后调用toString方法时还会new String()。

    内存中由于创建了较多的StringBuilder和String对象,还有一方面是内存占用,调用GC还会额外花费时间。

    所以,字符串拼接直接使用StringBuilder会大大提高性能,尤其是多个字符串拼接。

    改进的空间:

    如果开发中基本确定拼接后的字符串长度不高于某个限定值,可以使用StringBuilder(int highlevel)构造器来进行创建StringBuilder。

     五、intern()的使用

    以上的解释:

    如果字符串常量池中,通过queals方法对比的时候,已经包含了这个字符串,接下来池子中的字符串将会被返回。否则就会将这个字符串加到池子里,并且返回这个字符串实例。

    假如说s和t是相同的,以下两种情况结果都为true。

    s.intern() == t.intern()

    s.equals(t)

    1.如果不是用双引号声明的String对象,可以使用String提供的intern方法:intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。

        比如:String s = new String("I love you").intern();

    2.也就是说,如果在任意字符串上调用String.intern方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。因此,下列表达式的值必定是true:

        ("a" + "b" + "c").intern() == "abc";

    3.通俗点讲,Interned String就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,加快字符串操作任务的执行速度。注意,这个值会被存放在字符串内部池(字符串常量池)(String Intern Pool)。

    如何保证变量s指向字符串常量池中的数据?

    1.String s = "abc"; // 字面量方式定义。

    2.String s = new String("abc").intern();

        String s = new StringBuilder("abc").toString().intern(); //调用intern方法。

    new String("abc")会创建几个对象?

    1.创建了两个对象

    一个是通过new关键字在堆空间创建的。

    另一个是ldc创建的字符串常量池中的对象。

    2.创建了5个对象

    第一个new 了StringBuilder对象(1)。

    第二个new了String(4)。

    第三个常量池中的a(6)。

    第四个new了String(9)。

    第五个常量池中的b(11)。

    第六个,StringBuilder的toString()方法,也会new一个String(toString方法的调用,并不会将字符串放到字符串常量池中)。

    StringBuilder的toString()方法:

    toString方法的调用,并不会将字符串放到字符串常量池中。

    我们并没有在字节码指令中发现ldc命令,所以并没有将字符串放到字符串常量中。

    经典题目(99%的人会做错!)

    结果:

    jdk6:false、false。

    jdk7和8:false、true。

    解释:

        Strign s = new String("1");我们已经知道,它不仅会new一个String对象在堆中,而且还会在字符串常量池中放上"1"。

        所以调用s.intern();并没有什么实际意义,因为字符串常量池中已经有"1"了。

        而且s指向的是new String("1")的地址,而不是常量池中的地址。

        如果s.intern() 改为s = s.intern(); 那么s == s2就是true了。

        s3的地址记录的实际是new String("11")。此时,字符串常量池中并不存在"11",只存在"1",因为字符串拼接使用StringBuilder之后调用的toString()方法,并不会将结果存在字符串常量池中。

        s3.intern();会将"11"存在字符串常量池中。jdk6中就是创建了一个常量池中新的对象"11",也就有新的地址;jdk7和8常量池放到堆中了,这里s3调用intern()方法,这里由于空间节省,常量池中并不会创建"11",而是创建了一个指向堆空间new String("11")的地址

        String s4 = "11"; s4指向的地址是上一行代码在常量池中存放的"11"。

        jdk7和8中,本质上s3和s4指向了堆中同一个地址

    详细过程:

        

    拓展:

    原因:

        String s4 = "11"; 是实打实的在字符串常量池中创建了"11",而不是一个引用了。

        s3.intern(); 因为常量池中已经有"11"了。

        此时s3指向堆中的new String("11"); s4指向常量池中的"11",s5也指向常量池中的"11"。

    总结String的intern()的使用:

    1.jdk1.6中,将这个字符串对象尝试放入字符串常量池。

        如果串池中有,则并不会放入,返回已有的串池中的对象的地址。

        如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址。

    2.jdk1.7起,将这个字符串对象尝试放入串池。   

        如果串池中有,则并不会放入,返回已有的串池中的对象地址。

        如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址。

    intern练习1:

    jdk6:true、false

    jdk7:true、true(jdk7在串池并没有创建"ab"对象,而是创建了一个引用,指向堆中的new String("ab")的地址)

    在代码前面加上String x = "ab";

    intern练习2:

        第一种情况是false,第二种情况是true。

        String s1 = new String("ab"); 会在堆中new 一个String,并且也会在字符串常量池中新建一个"ab"。

        String s1 = new String("a") + new String("b"); 会在字符串常量池中新建"a"、"b",拼接字符串使用StringBuilder,其toString()方法并不会在常量池中创建"ab"。而且调用s1.intern()之后,jdk7由于堆中已经有ab字符串了,在字符串常量池只创建了一个该对象的引用。

    六、intern()效率测试

    空间角度(jdk1.8)

    使用jvisualvm查看结果:

    不使用intern:

    使用intern:

    总结:使用intern更节省内存空间(尤其是程序中有大量重复的字符串时),并且执行速度还快。

    七、StringTable的垃圾回收

    使用jvm参数:

    -XX:+PrintStringTableStatistics,打印字符串常量池信息。

    main函数中什么也不写的字符串常量池打印信息:

    执行以下代码:

    执行以下代码:

    我们会发现发生了GC,并且字符串常量池数据量降了下来

    八、G1中的String去重操作

    1.背景:对许多Java应用(有大的也有小的)做的测试得出以下结果:
        堆存活数据集合里面String对象占了25%。
        堆存活数据集合里面重复的String对象有13.5%。
        String对象的平均长度是45。
    2.许多大规模的Java应用的瓶颈在于内存,测试表明,在这些类型的应用里面,Java堆中存活的数据集合差不多25%是String对象。更进一步,这里面差不多一半String对象是重复的,重复的意思是说:string1.equals(string2)=true。堆上存在重复的String对象必然是一种内存的浪费。这个项目将在G1垃圾收集器中实现自动持续对重复的String对象进行去重,这样就能避免浪费内存。

    实现

    1.当垃圾收集器工作的时候,会访问堆上存活的对象。对每一个访问的对象都会检查是否是候选的要去重的String对象。
    2.如果是,把这个对象的一个引用插入到队列中等待后续的处理。一个去重的线程在后台运行,处理这个队列。处理队列的一个元素意味着从这个队列删除这个元素,然后尝试去重它引用的String对象。
    3.使用一个hashtable来记录所有的被String对象使用的不重复的char数组。当去重的时候,会查这个hashtable,来看堆上是否已经存在一个一模一样的char数组。
    4.如果存在,String对象会被调整引用那个数组,释放对原来的数组的引用,最终会被垃圾收集器回收掉。
    5.如果查找失败,char数组会被插入到hashtable,这样以后的时候就可以共享这个数组了。

    命令行选项

        UseStringDeduplication(bool):开启String去重,默认是不开启的,需要手动开启。
        PrintStringDeduplicationStatistics(bool):打印详细的去重统计信息。
        StringDeduplicationAgeThreshold(uintx):达到这个年龄的String对象被认为是去重的候选对象。

    展开全文
  • String intern()方法

    2021-01-18 09:50:00
    Q2:如何理解String.intern()方法? A1:若常量池中已经存在 “cyou”,则直接引用,也就是此时只会创建一个对象,如果常量池中不存在 “cyou”,则先创建后引用,也就是有两个。 A2:当一个 String 实例调用...
  • String的intern方法详解

    2019-02-20 09:58:59
    * <code>s.intern() == t.intern()</code> is <code>true</code> * if and only if <code>s.equals(t)</code> is <code>true</code>. * <p> * All literal strings and string-valued constant expressions ...
  • intern-saucelabs-源码

    2021-07-12 21:14:50
    在本教程中,我们将介绍如何使用 Intern 设置、编写测试和运行测试。 这个存储库包含一个非常基本的 Hello World 演示“应用程序”,我们将使用它作为构建示例。 为了完成本教程,您将需要以下内容: 与 Bourne ...
  • intern-browserstack-源码

    2021-07-12 20:38:53
    在本教程中,我们将介绍如何使用 Intern 设置、编写测试和运行测试。 这个存储库包含一个非常基本的 Hello World 演示“应用程序”,我们将使用它作为构建示例。 为了完成本教程,您将需要以下内容: 与 Bourne ...
  • String的Intern方法详解

    2020-12-18 17:20:41
    引言  在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。...intern 方法会从字符串常量池中查询当前字符串是否存在,若
  • 一、intern()方法的理解(重难点) 1、intern()是字符串对象的一个方法,它底层是一个native直接是调用了本地方法 2、调用这个方法之后就是去看当前字符串是否在常量池中存在 (1)存 在:那就直接返回该字符串在...
  • 深入解析String#intern

    2020-08-06 08:36:21
    在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。...intern 方法会从字符串常量池中查询当前字符串是否存在,若不..
  • Java之intern方法

    千次阅读 2020-08-01 18:13:02
    面试问题: (1)现在当有人问 String str = new String(“abc”);创建了几个对象,常量池...(3)new String(“abc”).intern();创建了几个对象(如果常量池里面已经有该字符串对象了就是1个,如果没有就是两个) ...
  • 这篇文章将要讨论 Java 6 中是如何实现 String.intern 方法的,以及这个方法在 Java 7 以及 Java 8 中做了哪些调整。
  • 理解Java字符串常量池与intern()方法 本文转载于作者:没课割绿地 出自:http://www.cnblogs.com/justcooooode/p/7603381.html String s1 = &amp;quot;Hello&amp;quot;; String s2 = &amp;quot;Hello&...
  • String中intern方法的作用

    万次阅读 多人点赞 2018-05-23 20:25:06
    读完这篇文章你可以了解,String在虚拟机内存中的存放,intern方法到底有什么用,这么多String对象的创建到底有什么区别,String 创建的对象有几个!! 进入正题 我们需要先了解一下 String str=”abc”;和 ...
  • 面试的时候经常被问到String的intern方法的调用及内存结构发生的变化。但在实际生产中真正用到过了吗,看到过别人如何使用了吗? 最近阅读Nacos的源码,还真看到代码中使用String类的intern方法,NamingUtils类中有...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 45,075
精华内容 18,030
关键字:

intern