当前位置 博文首页 > 业余敲代码的博客:java String、StringBuffer和StringBuilder的

    业余敲代码的博客:java String、StringBuffer和StringBuilder的

    作者:[db:作者] 时间:2021-09-12 12:07

    一、String 类——String字符串常量

    1、概念:

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

    String a = "123";
    a= "456";
    

    在这里插入图片描述

    如图所示,再次给a赋值时,并不是对原来堆中实例对象进行重新赋值,而是生成一个新的实例对象,并且指向“456”这个字符串,a则指向最新生成的实例对象,之前的实例对象仍然存在,如果没有被再次引用,则会被垃圾回收

    2、总结:

    String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。

    3、思考点:

    下面这个例子一共需要开辟几次内存空间呢?(三次)

    String s = new String("aa");
    s = s + "bb"
    

    初始String值为“aa”,然后在这个字符串后面加上新的字符串“bb”,这个过程是需要重新在栈堆内存中开辟内存空间的,最终得到了“aabb”字符串也相应的需要开辟内存空间,这样短短的两个字符串,却需要开辟三次内存空间,不得不说这是对内存空间的极大浪费。为了应对经常性的字符串相关的操作,就需要使用Java提供的其他两个操作字符串的类——StringBuffer类和StringBuild类来对此种变化字符串进行处理

    4、字符串常量池:

    (1)概念:

    字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。

    (2)区分点:

    问题1:String str="i"与 String str=new String(“i”)一样吗?
    不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中

    问题2: String s = new String(“xyz”);创建了几个字符串对象
    两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象

    (3)string 关于== 的易错判断题:

    String str1 = "a"; //str1指向静态区
    String str2 = new String("a");  //str2指向堆上的对象
    String str3 = "a";
    String str4 = new String("a");
    System.out.println(str1.equals(str2)); //true
    System.out.println(str2.equals(str4)); //true
    System.out.println(str1 == str3); //true
    System.out.println(str1 == str2); //false
    System.out.println(str2 == str4); //false
    System.out.println(str2 == "a"); //false
    
    

    5、Java9对于字符串的改进

    Java9以后改进了字符串(包括String、StringBuffer、StringBuilder)的实现,在Java9以前字符串采用char[]数组来保存字符,因此字符串的每个字符占2字节;而Java9的字符串采用byte[]数组再加一个encoding-flag字段来保存字符,因此字符串的每个字符只占1字节。所以Java9的字符串更加节省空间,字符串的功能方法也没有受到影响

    二、StringBuffer和 StringBuilder 类:

    1、使用场景:

    (1)当对字符串进行修改的时候,特别是字符串对象经常改变的情况下,需要使用 StringBuffer 和 StringBuilder 类;
    StringBuffer 字符串变量(线程安全)
    StringBuilder 字符串变量(非线程安全)

    (2)和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象

    (3)由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类

    (4)AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法

    2、使用方法:

    (1)StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符
    (2)java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同

    3、StringBuffer是如何实现线程安全的呢?

    StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全,如下图所示:
    在这里插入图片描述

    三、区别与联系:

    1、继承关系:

    在这里插入图片描述

    可见,StringBuffer和 StringBuilder均继承至AbstractStringBuilder

    2、字符修改区别:

    (1)String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象;
    (2)如果要操作少量的数据用 String; 字符串对象经常改变的情况下,需要使用 StringBuffer 和 StringBuilder 类
    (3)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
    单线程操作字符串缓冲区下操作大量数据 StringBuilder

    3、初始化上的区别:

    String可以空赋值,StringBuffer 和 StringBuilder不行;
    StringBuffer s = new StringBuffer(“hello”);//创建带有内容的StringBuffer对象,对象的内容就是字符串”

    cs