美文网首页
还有人不了解String?

还有人不了解String?

作者: 有没有口罩给我一个 | 来源:发表于2019-05-18 19:45 被阅读0次

    大家去面试的时候,有没有被问到下面关于string的题?我相信都有。

    String s3 = new String("1") + new String("1");

    内存有几个对象?两个?三个?四个?答案:四个。

    来了解两个概念方法区和运行时常量,抄自深入理解Java虚拟机:

    • 方法区:
      它用于存储已经被虚拟机加载的类信息、常量、静态变量,即编译器编译后的代码等数据。这块区域的内存回收目标主要针对常量池和类型的回收,在JDK中已经将字符串常量移出永久代。

    • 运行时常量池:
      运行时常量池也是方法区的一部分,Class文件出了有类的版本、字段、方法、接口等描述信息外,还有意向信息就是常量池,用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池。当然并非Class文件中的常量的内容才能进入防区的运行时常量池,运行期间也可能将新的常量放入常量池,如:String.intern()方法。

    上面都是些概念性,举个栗子?

     //首先在堆内存中创建了一个对象,然后在常量池中查找equals "1"的对象,如果没有则创建一个对象保存在常量池中,有的话则不管
       String s1 = new String("aaa");
        // 在常量池中
        String s2 = "aaa";
       Log.e("tag","1"+ String.valueOf(s1 == s2));   // false
    
    
        // 首先在堆内存中创建了一个对象,然后在常量池中查找equals "1"的对象,如果没有则创建一个对象保存在常量池中,有的话则不管,之后调用intern()直接返回常量池中的引用
        s1 = new String("bbb").intern();
        s2 = "bbb";
       Log.e("tag","2"+ String.valueOf(s1 == s2));   // true
    
        //都是返回常量池中的引用
        s1 = "ccc";
        s2 = "ccc";
       Log.e("tag","3"+ String.valueOf(s1 == s2));   // true
    
    
        //调用intern()直接返回常量池中的引用
        s1 = new String("ddd").intern();
        s2 = new String("ddd").intern();
       Log.e("tag","4"+ String.valueOf(s1 == s2));   // true
    
    
        // 直接返回常量池中的引用,只要有任何一个不是字符串字面常量形式,都不会在常量池生成"aabb"
        s1 = "ab" + "cd";
        s2 = "abcd";
       Log.e("tag","5"+ String.valueOf(s1 == s2));   // true
    
    
        // 
        String temp = "hh";
        s1 = "a" + temp;//只要有任何一个不是字符串字面常量形式,都不会在常量池生成"ahh",且此时jvm做了优化,不会同时生成"a"和"hh"在字符串常量池中
        // 如果调用s1.intern 则最终返回true
        s2 = "ahh";//常量池中
       Log.e("tag","6"+ String.valueOf(s1 == s2));   // false
    
        temp = "hh".intern();//"hh" 被加入常量池中
        s1 = "a" + temp;//
        s2 = "ahh";
       Log.e("tag","7"+ String.valueOf(s1 == s2));   // false
    
        temp = "hh".intern();
        s1 = ("a" + temp).intern();
        s2 = "ahh";
       Log.e("tag","8"+ String.valueOf(s1 == s2));   // true
    
        s1 = new String("1");    // 同时会生成堆中的对象 以及常量池中1的对象,但是此时s1是指向堆中的对象的
        s1.intern();            // 常量池中的已经存在
        s2 = "1";
       Log.e("tag","9"+ String.valueOf(s1 == s2));   // false
    
        String s3 = new String("1") + new String("1");    // 此时生成了四个对象 常量池中的"1" + 2个堆中的"1" + s3指向的堆中的对象(注此时常量池不会生成"11")
        s3.intern();    // jdk1.7之后,常量池不仅仅可以存储对象,还可以存储对象的引用,会直接将s3的地址存储在常量池
        String s4 = "11";    // jdk1.7之后,常量池中的地址其实就是s3的地址
        Log.e("tag","10"+ String.valueOf(s3 == s4)); // jdk1.7之前false, jdk1.7之后 true
    
        s3 = new String("2") + new String("2");
        s4 = "22";        // 常量池中不存在22,所以会新开辟一个存储22对象的常量池地址
        s3.intern();    // 常量池22的地址和s3的地址不同
        Log.e("tag","11"+ String.valueOf(s3 == s4)); // false
    

    对于什么时候会在常量池存储字符串对象,结论:

    1. 显示调用String的intern方法的时候;
    2. 直接声明字符串字面常量的时候,例如: String a = "aaa";
    3. 字符串直接常量相加的时候,例如: String c = "aa" + "bb";
      其中的aa或bb只要有任何一个不是字符串字面常量形式,都不会在常量池生成"aabb"且此时jvm做了优化,不会同时生成"aa"和"bb"在字符串常量池中

    相关文章

      网友评论

          本文标题:还有人不了解String?

          本文链接:https://www.haomeiwen.com/subject/wntxzqtx.html