Jvm-7—String

        7.1 String的不可变性

        String 在jdk8及以前内部使用final char [],jdk9之后改成byte[].

        通过字面量初始化String时,字符串值存储在字符串常量池。字符串常量池不允许存储相同内容得字符串值。字符串常量池底层是一个Hash Table.(数组+链表)。当数组长度过小时,会造成Hah冲突严重,影响字符串值得插入效率。使用-XX:StringTableSize可设置Hash Table得数组长度。

        7.2 字符串拼接操作

        常量与常量的拼接结果存在常量池,原理是编译期优化;

        只要拼接对象中有一个是变量,结果就存在堆中,原理是StringBuilder;

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

     void test1(){
        String s1 = "a"+"b"+"c";
        String s2 = "abc";
        System.out.println(s1 == s2);//true
    }

    void test2(){
        String s1 = "javaEE";
        String s2 = "hadoop";

        String s3 = "javaEEhadoop";
        String s4 = s1+s2;
        String s5 = s1 + "hadoop";
        String s6 = s5.intern();
        System.out.println(s3 == s4);//false
        System.out.println(s3 == s5);//false
        System.out.println(s3 == s6);//true

    }
    
    void test3(){
      final String s1 = "javaEE";
      final String s2 = "hadoop";  
      String s3 = "javaEE" + "hadoop"; 
      String s4 = "javaEEhadoop";
      System.out.println(s3 == s4);//true
    }

test1:字节码文件。对于常量的拼接  在编译器就已经确定。

 0 ldc #6 <abc>
 2 astore_1
 3 ldc #6 <abc>
 5 astore_2

test2:字节码文件  字符串拼接时,含有变量时  相当于new了一个新的对象。

  0 ldc #9 <javaEE>
  2 astore_1
  3 ldc #10 <hadoop>
  5 astore_2
  6 ldc #11 <javaEEhadoop>  //生成s3
  8 astore_3
  9 new #12 <java/lang/StringBuilder>   //s1 + s2 new了一个新的对象
 12 dup
 13 invokespecial #13 <java/lang/StringBuilder.<init>>
 16 aload_1
 17 invokevirtual #14 <java/lang/StringBuilder.append>  //将s1 append
 20 aload_2
 21 invokevirtual #14 <java/lang/StringBuilder.append>  //将s2  append
 24 invokevirtual #15 <java/lang/StringBuilder.toString> //调用toString ===>近似于new String("")
 27 astore 4    //赋值给s4
 29 new #12 <java/lang/StringBuilder>   //s1 + "hadoop"
 32 dup
 33 invokespecial #13 <java/lang/StringBuilder.<init>>
 36 aload_1
 37 invokevirtual #14 <java/lang/StringBuilder.append>
 40 ldc #10 <hadoop>
 42 invokevirtual #14 <java/lang/StringBuilder.append>
 45 invokevirtual #15 <java/lang/StringBuilder.toString>
 48 astore 5   //赋值给s5
 50 aload 5
 52 invokevirtual #16 <java/lang/String.intern>
 55 astore 6
 57 getstatic #7 <java/lang/System.out>
 60 aload_3
 61 aload 4
 63 if_acmpne 70 (+7)
 66 iconst_1
 67 goto 71 (+4)
 70 iconst_0
 71 invokevirtual #8 <java/io/PrintStream.println>
 74 getstatic #7 <java/lang/System.out>
 77 aload_3
 78 aload 5
 80 if_acmpne 87 (+7)
 83 iconst_1
 84 goto 88 (+4)
 87 iconst_0
 88 invokevirtual #8 <java/io/PrintStream.println>
 91 getstatic #7 <java/lang/System.out>
 94 aload_3
 95 aload 6
 97 if_acmpne 104 (+7)
100 iconst_1
101 goto 105 (+4)
104 iconst_0
105 invokevirtual #8 <java/io/PrintStream.println>
108 return

        7.3 拼接操作 与 append操作效率对比

        变量拼接时,需大体经过3个步骤

        1 创建StringBuilder对象

        2 执行append操作

        3 调用toString() 返回给新的变量

         所以创建StringBuilder对象  执行append操作效率更高。同时 可以执行StringBuilder的有参构造,执行byte数组的大小 防止不断扩容。

        StringBuffer、StringBuilder为字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象,一个不可变的对象。

        7.4 intern

        调用intern()方法后, 会去字符串常量池中调用equals方法判断是否含有当前字符串值,如果没有则创建后返回其地址;如果已经有了则直接返回地址。

        如何保证变量s指向字符串常量池。、

        1 使用字面量赋值  String s = "123";

        2 调用intern方法   String s  = new String("123").intern();

        7.5 new String()到底创建了几个对象?

        String a = new String("AB");

        两个对象 字符串常量池中AB  栈中a

        String a = new String("A") + new String("B");     

         六个对象: 常量池中A     B

        字符串拼接  StringBuilder 对象

        new String(A)   new String("B")

        StringBuilder.toString()返回的对象 a

        7.6 intern面试题

        

    void test3(){
        String s1 = new String("1"); //在常量池中创建一个"1" 然后创建一个String对象 将String对象的地址赋值给s1
        s1.intern(); //常量池中已创建  此代码没什么作用
        String s2 = "1"; //s2 指向 常量池 "1"
        System.out.println(s1 == s2); //false  s1指向String对象  s2指向字符串常量池

        String s5 = new String("2").intern(); //在常量池中创建一个"2" s5指向常量池中2的地址
        String s6 = "2"; //s2 指向 常量池 "2"
        System.out.println(s5 == s6); //true 

        String s3 = new String("1") + new String("1"); //s3 指向 new StringBuilder("11").toString()返回的对象地址 此时常量池中不存在常量11
        s3.intern(); //创建常量11
        String s4 = "11"; //s4 指向字符串常量池中的11
        System.out.println(s3 == s4);//jdk6 false   jdk7/8 true
        // jdk6中 把此对象复制一份 并返回串中的对象地址
        //jdk6中 把此对象引用地址复制一份 并返回  池中存的是s3对象的引用地址 s3 s4指向同一对象
        String s7 = new String("3") + new String("3"); //s7 指向 new StringBuilder("33").toString()返回的对象地址 此时常量池中不存在常量33
        String s8 = "33"; //s8 指向字符串常量池中的33
        String s9 = s7.intern();
        System.out.println(s7 == s8);// false   
        System.out.println(s9 == s8);// true 
        
    }

        

                

                对于存在大量的重复的字符串时  使用intern方法 可大幅降低内存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值