• JVM理论:(三/8)关于String、StringBuffer、StringBuilder、final


      以下是从网上整理出的资料,不同作者对某些具体描述有略微差别,有的具体描述可能会存在不准确的情况,但结论是一致的。

    一、String

      equals比较值,==比较引用,我们主要关注引用的比较。

    1、两种方式创建String的过程

    String s1="abc";
    String s2="abc";
    String s3 = new String("abc");
    System.out.println(s1==s2);
    System.out.println(s1==s3);
    
    输出结果:
    true
    false

      对于 String s1="abc"; 这种方式,如果常量池中已经存在字符串“abc”,会直接引用这个字符串,否则会创建一个“abc”字符串,所以对于s1、s2来说,引用指向的地址是一样的,返回true。相当于s1,s2 —>常量池中的字符串。(不确定的是,是不是栈中的引用直接指向常量池中的地址)

      对于 String s3 = new String("abc"); 这种方式,无论如何都会在堆中new一个String("abc")对象,当然如果常量池中没有"abc"字符串,也会先创建。相当于s3 —>堆中的对象 —>常量池中的字符串。

      s3指向的是堆中的对象,明显s1与s3指向的地址不同,所以返回false。  String s1="abc"; 在编译阶段就被确定, String s3 = new String("abc"); 在运行时才能确定。

    2、String不可变

      java.lang.String类使用了final修饰,不能被继承。Java程序中的所有字面值,即双引号括起的字符串,如"abc",都是作为String类的实例实现的。String是常量,其对象一旦构造就不能再被改变。换句话说,String对象是不可变的,每一个看起来会修改String值的方法,实际上都是创造了一个全新的String对象,以包含修改后的字符串内容,而最初的String对象则丝毫未动。

    String s = "abc";
    s = "123";
    System.out.println("s=" + s);
    
    输出结果:
    s=123

      这里看似变量s被修改了,实际上将s重新指向一个字符串“123”,而不是直接将原来的"abc"修改为"123"。

    3、String的相加运算

    String s1 = "abc";
    String s2 = "ab" + "c";
    System.out.println(s1==s2);
    
    final String  s3 = "ab";
    String s4 = s3 + "c";
    System.out.println(s1==s4);
    
    String s5 = "ab";
    String s6 = s5 + "c";
    System.out.println(s1==s6);
    
    String s7 = new String("ab") + "c";
    System.out.println(s1==s7);
    
    输出结果:
    true
    true
    false
    false

      第1,2个结果是true是因为,编译器对那些在编译期就可以确定其值的常量的运算会进行优化,因为s2+号两边是字面量,所以s2在编译期就会被优化为 String s2 = "abc";

      对于第3、4个结果,运算中存在变量,虚拟机对+的运算就会被解析为StringBuilder(或 StringBuffer)类及其 append 方法实现的,再通过toString 方法转换为字符串,所以s6相当于 s6=new StringBuffer(s5).append("c").toString(),而s6重新指向一个新的StringBuffer对象。

    //方式一
    String s = "s";
    for (int i = 0; i < 20; i++) {
        s += i;
    }
    //方式二
    StringBuffer sb = new StringBuffer("s");
    for (int i = 0; i < 20; i++) {
        sb.append(i);
    }

      结合我们平时的应用,如果用String+的方式,每循环一次,就会重新new一个StringBuffer对象,无疑造成了很多不必要的开销,而方式二会更高效一点。

    二、String,StringBuilder,StringBuffer三者的区别

      运行速度快慢为:StringBuilder > StringBuffer > String  

    String与StringBuilder、StringBuffer的区别:

      String为字符串常量,即String对象一旦创建之后该对象是不可更改的,前文中对于存在变量的String+操作,实质是new StringBuffer和.toString()操作。而StringBuilder和StringBuffer都可以直接对原对象追加、插入和删除。

    StringBuilder与StringBuffer的区别:

      StringBuilder是线程不安全的。而StringBuffer是线程安全的 ,StringBuffer中很多方法可以带有synchronized关键字。

     综上,

      String:适用于少量的字符串操作的情况

      StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

      StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

    三、final关键字

    1)被final修饰的类不可以被继承
    2)被final修饰的方法不可以被重写,JVM会尝试为之寻求内联。
    3)被final修饰的变量不可以被改变,在编译阶段会存入调用类的常量池中。
      
    对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
    被final修饰的变量不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的。
     
    final标记的成员变量必须在声明的同时赋值,如果在声明的时候没有赋值,那么只有一次赋值的机会,而且只能在构造方法中显式赋值,然后才能使用
    final标记的局部变量可以只声明不赋值,也只能进行一次赋值


    参考链接:  

    https://www.cnblogs.com/baihehanqiu/p/6938850.html

    http://www.cnblogs.com/avivahe/p/5747127.html

    https://segmentfault.com/a/1190000009888357

    https://blog.csdn.net/wang854837173/article/details/52439123

    https://www.cnblogs.com/qiaoyanlin/p/6877628.html

    https://www.cnblogs.com/tianyaxue/p/3145079.html

    https://blog.csdn.net/sugar_rainbow/article/details/68150249

    http://www.cnblogs.com/roy-mustango/p/4240302.html

    https://www.cnblogs.com/hithlb/p/4872373.html

    String类型相加

    https://blog.csdn.net/changjinglubeipan/article/details/78614969

    https://www.zhihu.com/question/35014775

    https://baijiahao.baidu.com/s?id=1576306750248354576&wfr=spider&for=pc

    关于string的面试题

    https://blog.csdn.net/uotail/article/details/71244606

    https://blog.csdn.net/nwpu_geeker/article/details/78701343

    StringBuffer

    https://www.cnblogs.com/su-feng/p/6659064.html

    final

    https://www.jianshu.com/p/17becea7942d

    https://www.cnblogs.com/dolphin0520/p/3736238.html

    https://www.cnblogs.com/xh0102/p/5729381.html

  • 相关阅读:
    OD: Kernel Vulnerabilities
    newInstance()和new的区别
    原型模式
    工厂模式
    代理模式
    策略模式
    简单工厂模式
    C#操作符的重载
    旅行之舌尖上的中国
    模式和原则[转载]
  • 原文地址:https://www.cnblogs.com/zjxiang/p/9379676.html
Copyright © 2020-2023  润新知