• Java 学习笔记(一)String、StringBuilder、StringBuffer


    原理性及JVM中字符串的存储方式可参考文章:https://www.cnblogs.com/goody9807/p/6516374.html (非常清晰,从JVM内存角度进行了解释)

    一、String

         在编译期中,即形成 class文件的时候,双引号定义的字符串常量就会在class文件中被解释成常量表。当被类加载器加载到JVM时,会给class对象开始分配内存,常量表会被放到方法区中,同时会在堆中的永生代创建常量表各个字符串对应的拘留(永驻)对象,该对象的值是常量表的字符串。

         String 值是不可变的,它是一个常量。当创建一个字符串时,会先判断常量池(堆中存储拘留对象)中是否有这个字符串,若没有,则创建拘留对象(存储常量值),同时在堆中创建该对象,该对象存储拘留对象中的值。

        PS:我们可以把上述说的拘留对象认为是在字符串常量池。

         字符串常量池是在堆(分为新生代、老年代、永生代)上的永生代(长驻留区)中。

    示例:

    1、String s = new String(“xyz”); //创建了几个对象

        创建了一个或两个,若常量池中有了 “xyz”,则在堆中创建一个对象,其value值为xyz,此时就创建了一个对象;若常量池中没有 xyz,则需创建,同时堆中也创建,此时就是2个对象。

        这个是在运行期间才创建的。

    2、String s = "a"+"b"+"c"+"d"; //创建了几个对象

       创建了1个或7个,和JVM本身有关。

       JVM会将这种当成 StringBuffer来处理,在它眼里就是 String s = "abcd";

       这个是在编译期中就已经创建好。

    3、String s = "abc";   //创建了几个对象

        创建了0个或1个,字符串常量池中若没有 abc会创建,然后 s 直接指向 abc在常量池中的内存地址。若有的话,直接指向常量池中的内存地址。不会在堆上再给 s 分配内存块空间,这也是与new String 不一样的地方。

    4、String a = "a";

          String b = "b";

          String c = a+b+"c";     //创建了几个对象

        在类加载分配内存时,字符串常量池中创建了 “a” "b",且 a、b 指向了字符串常量池对应的内存地址。由于c是引用变量,在运行期中,在字符串常量池中创建了 "c",接着使用 StringBuffer 来进行拼接。

        相当于:

           String c = new StringBuffer(a).append(b).append(“c”).toString();

    建议:

     1) 在平时构建String 时,第三种方式效率高于 第一种;

     2) 字符串在运行过程中经常改变的话,不建议使用 string 对象,如果用String保存一个经常被修改的字符串,该字符串每次修改时都会创建新的无用的对象,这些无用的对象会被垃圾回收器回收,会影响程序的性能,不建议这么做。

    二、StringBuffer

       它是线程安全的可变字符序列,一个类似String的字符缓冲区。可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。

    三、StringBuilder

        用法和 StringBuffer差不多,区别在于其非线程安全。

    四、三者的区别

    1、String 和 StringBuffer

         String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。

       但是在一的 3 示例例外,速度和 StringBuffer一样。

        在 一 的4示例中,String c = a+b 使用方式和 StringBuffer类似,但是从4个整个例子来看,产生了多个无用的字符串常量对象。总的来说无 StringBuffer好用;

    2、StringBuffer 和 StringBuilder

       StringBuffer是线程安全的 
       StringBuilder(5.0版本后添加的类,是StringBuffer的一个简单替换)为非线程安全的,但是效率会好些,在单线程环境中要做大量字符串累加时推荐使用该类

    Java提供了String、StringBuffer和StringBuilder类来封装字符串,并提供了一系列操作字符串对象的方法。

    它们的相同点是都用来封装字符串;都实现了CharSequence接口。它们之间的区别如下:(以下来源:https://cloud.tencent.com/developer/article/1414756)

    一、可变与不可变

    String类是一个不可变类,即创建String对象后,该对象中的字符串是不可改变的,直到这个对象被销毁。StringBuffer与StringBuilder都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,是可变类。

    由于String是可变类,适合在需要被共享的场合中使用,当一个字符串经常被修改时,最好使用StringBuffer实现。如果用String保存一个经常被修改的字符串,该字符串每次修改时都会创建新的无用的对象,这些无用的对象会被垃圾回收器回收,会影响程序的性能,不建议这么做。

    二、初始化方式

    当创建String对象时,可以利用构造方法String str = new String("Java")的方式来对其进行初始化,也可以直接用赋值的方式String s = "Java"来初始化。而StringBuffer只能使用构造方法StringBuffer sb = new StringBuffer("hello")的方式初始化。

    三、字符串修改方式

    String字符串修改方法是首先创建一个StringBuffer,其次调用StringBuffer的append方法,最后调用StringBuffer的toString()方法把结果返回,示例代码如下:

    String str = "hello";
    str += "java";

    以上代码等价于下面的代码:

    StringBuffer sb = new StringBuffer(str);
    sb.append("java");
    str = sb.toString();

    上述String字符串的修改过程要比StringBuffer多一些额外操作,会增加一些临时的对象,从而导致程序的执行效率降低。StringBuffer和StringBuilder在修改字符串方面比String的性能要高。

    四、是否实现了equals和hashCode方法

    String实现了equals()方法和hashCode()方法,new String("java").equals(new String("java"))的结果为true;

    而StringBuffer没有实现equals()方法和hashCode()方法,因此,new StringBuffer("java").equals(new StringBuffer("java"))的结果为false,将StringBuffer对象存储进Java集合类中会出现问题。

    五、是否线程安全

    StringBuffer与StringBuilder都提供了一系列插入、追加、改变字符串里的字符序列的方法,它们的用法基本相同,只是StringBuilder是线程不安全的,StringBuffer是线程安全的,。如果只是在单线程中使用字符串缓冲区,则StringBuilder的效率会高些,但是当多线程访问时,最好使用StringBuffer。

    综上,在执行效率方面,StringBuilder最高,StringBuffer次之,String最低,对于这种情况,一般而言,如果要操作的数量比较小,应优先使用String类;如果是在单线程下操作大量数据,应优先使用StringBuilder类;如果是在多线程下操作大量数据,应优先使用StringBuilder类。

  • 相关阅读:
    研究生
    linux下C++开发工具
    GCC and G++ install
    linux yum命令详解
    OpenCV:SURF算法浅析
    linux内核(kernel)版本号的意义
    常见排序算法
    bash:command not found
    C++ 面向对象(数据封装)
    HTML5 script 标签的 crossorigin 和integrity属性的作用
  • 原文地址:https://www.cnblogs.com/sandyflower/p/12152570.html
Copyright © 2020-2023  润新知