美文网首页
Java String2

Java String2

作者: 高岳_bdf5 | 来源:发表于2018-06-11 08:34 被阅读0次

    下面程序的运行结果是什么?

    String stra = "ABC";
    String strb = new String("ABC");
    System.out.println(stra == strb);    //1,false
    System.out.println(stra.equals(strb));    //2,true
    

    对于 1 和 2 中两个都是显式创建的新对象,使用 == 总是不等,String 的 equals 方法有被重写为值判断,所以 equals 是相等的。

    String str1 = "123";
    System.out.println("123" == str1.substring(0));    //3,true
    System.out.println("23" == str1.substring(1));    //4,false
    
    对于 3 和 4 中 str1 的 substring 方法实现里面有个 index == 0 的判断,当 index 等于 0 就直接返回当前对象,否则新 new 一个 sub 的对象返回,而 == 又是地址比较,所以结果如注释。源码如下: 屏幕快照 2018-06-11 上午8.17.53.png
    String str5 = "NPM";
    String str6 = "npm".toUpperCase();
    System.out.println(str5 == str6);    //7,false
    System.out.println(str5.equals(str6));    //8,true
    

    toUpperCase 方法内部创建了新字符串对象,可以简单看下源码:

    public String toUpperCase(Locale var1) {
    ...
    return new String(var15, 0, var3 + var4);
    }
    
    String str9 = "a1";
    String str10 = "a" + 1;
    System.out.println(str9 == str10);    //11,true
    

    对于 11 来说当两个字符串常量连接时(相加)得到的新字符串依然是字符串常量且保存在常量池中只有一份。

    String str11 = "ab";
    String str12 = "b";
    String str13 = "a" + str12;
    System.out.println(str11 == str13);    //12,false
    

    对于 12 来说当字符串常量与 String 类型变量连接时得到的新字符串不再保存在常量池中,而是在堆中新建一个 String 对象来存放,很明显常量池中要求的存放的是常量,有 String 类型变量当然不能存在常量池中了。

    String str14 = "ab";
    final String str15 = "b";
    String str16 = "a" + str15; 
    System.out.println(str14 == str16);    //13,true
    

    对于 13 来说此处是字符串常量与 String 类型常量连接,得到的新字符串依然保存在常量池中,因为对 final 变量的访问在编译期间都会直接被替代为真实的值。

    private static String getBB() {   
        return "b";   
    }
    String str17 = "ab";   
    final String str18 = getBB();   
    String str19 = "a" + str18;   
    System.out.println(str17 == str19);    //14,false
    

    对于 14 来说 final String str18 = getBB() 其实与 final String str18 = new String(“b”) 是一样的,也就是说 return “b” 会在堆中创建一个 String 对象保存 ”b”,虽然 str18 被定义成了 final,但不代表是常量,因为虽然将 str18 用 final 修饰了,但是由于其赋值是通过方法调用返回的,那么它的值只能在运行期间确定,因此指向的不是同一个对象,所以可见看见并非定义为 final 的就保存在常量池中,很明显此处 str18 常量引用的 String 对象保存在堆中,因为 getBB() 得到的 String 已经保存在堆中了,final 的 String 引用并不会改变 String 已经保存在堆中这个事实;对于 str18 换成 final String str18 = new String("b"); 一样会返回 false,原因同理。

    String str20 = "ab";
    String str21 = "a";   
    String str22 = "b";   
    String str23 = str21 + str22;   
    System.out.println(str23 == str20);    //15,false
    System.out.println(str23.intern() == str20);    //16,true
    System.out.println(str23 == str20.intern());    //17,false
    System.out.println(str23.intern() == str20.intern());    //18,true
    

    对于 15 到 18 来说 str23 == str20 就是上面刚刚分析的,而对于调用 intern 方法如果字符串常量池中已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定)则返回字符串常量池中的字符串,否则将此 String 对象添加到字符串常量池中,并返回此 String 对象的引用,所以 str23.intern() == str20 实质是常量比较返回 true,str23 == str20.intern() 中 str23 就是上面说的堆中新对象,相当于一个新对象和一个常量比较,所以返回 false,str23.intern() == str20.intern() 就没啥说的了,指定相等。

    注释 11 到 14 深刻的说明了我们在代码中使用 String 时应该留意的优化技巧!特别说明 String 的 + 和 += 在编译后实质被自动优化为了 StringBuilder 和 append 调用,其实在Java中是先构建一个StringBuiler对象,然后使用append()方法拼接字符串最后调用toString()方法生成字符串。但是如果在循环等情况下调用 + 或者 += 就是在不停的 new StringBuilder 对象 append 了,这是及其浪费的,应该直接创建StringBuilder来实现。

    通过这些题说明要想玩明白 Java String 对象的核心其实就是玩明白字符串的堆栈和常量池,虚拟机为每个被装载的类型维护一个常量池,常量池就是该类型所用常量的一个有序集合,包括直接常量(String、Integer 和 Floating Point 常量)和对其他类型、字段和方法的符号引用,池中的数据项就像数组一样是通过索引访问的,由于常量池存储了相应类型所用到的所有类型、字段和方法的符号引用,所以它在 Java 程序的动态链接中起着核心的作用。

    相关文章

      网友评论

          本文标题:Java String2

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