java基础之String、StringBuilder、StringBuffer的异同

2023-12-31 17:53:17

目录

概述

String为何不可变,StringBuilder和StringBuffer为何可变

StringBuffer为何线程安全的,而StringBuilder为何非线程安全

三者性能分析

使用场景


概述

String、StringBuilder和StringBuffer都是用来处理字符串的类,底层都是通过char[]数组实现的。(jdk1.8及之前)

String是不可变的(线程安全的),StringBuilder和StringBuffer是可变的。StringBuffer是线程安全的,而StringBuilder是非线程安全的。具体如下:

String对象一旦创建,其值是不能修改的,如果要修改,会重新开辟内存空间来存储修改之后的对象;而StringBuffer和StringBuilder对象的值是可以被修改的;

StringBuffer几乎所有的方法都使用synchronized实现了同步,线程比较安全,在多线程系统中可以保证数据同步,但是效率比较低;而StringBuilder 没有实现同步,线程不安全,在多线程系统中不能使用 StringBuilder,但是效率比较高。

如果我们在实际开发过程中需要对字符串进行频繁的修改,不要使用String,否则会造成内存空间的浪费;当需要考虑线程安全的场景下使用 StringBuffer,如果不需要考虑线程安全,追求效率的场景下可以使用 StringBuilder。

下面我们从三个维度:可变性、线程安全性、性能来分析区别。

String为何不可变,StringBuilder和StringBuffer为何可变

String部分源码


public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    //。。。。。。。。。。。。。。。。。。
}

String类被声明为final,这意味着它不能被继承。那么他里面的方法就是没办法被覆盖的。
用final修饰字符串内容的char[]?(从JDK 1.9开始,char[]变成了byte[]),由于该数组被声明为final,一旦数组被初始化,就不能再指向其他数组
String类没有提供用于修改字符串内容的公共方法。例如,没有提供用于追加、删除或修改字符的方法。如果需要对字符串进行修改,会创建一个新的String对象。所以说String是不可变的。

与String不同StringBuilder和StringBuffer底层封装的char[]并没有用final修饰,这意味着它是可以修改的。

StringBuilder部分源码

public final class StringBuilder
    extends AbstractStringBuilder
    implements Serializable, CharSequence
{
    //。。。。。。。。。。。。。。。。。
}
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    //。。。。。。。。。。。。。
}

StringBuffer部分源码

public final class StringBuffer
    extends AbstractStringBuilder
    implements Serializable, CharSequence
{
    //。。。。。。。。。。。。。。。。
}
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;
    //。。。。。。。。。。。。。。。。。。。。。。。
}

StringBuffer为何线程安全的,而StringBuilder为何非线程安全

StringBuffer是线程安全的,它的方法都使用了synchronized关键字进行同步。在每个方法内部,通过加锁(synchronized)来确保在多线程环境下的安全访问。这意味着当一个线程访问StringBuffer的方法时,其他线程需要等待锁释放后才能执行相应的方法。因此,StringBuffer适用于多线程环境下对字符串的修改操作。

    //StringBuffer部分源码
    @Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }

    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

而StringBuilder则没有使用synchronized关键字进行同步,它是非线程安全的。这样可以提高性能,因为不需要进行锁的获取和释放操作。但是,在多线程环境下,如果有多个线程同时访问和修改StringBuilder对象,可能会导致数据不一致或出现竞态条件的问题。因此,StringBuilder适用于单线程环境下对字符串的修改操作。

    
    //StringBuilder部分源码
    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

三者性能分析

String:由于String是不可变的,每次对String进行修改操作时,都会创建一个新的String对象。这可能导致频繁的对象创建和垃圾回收,影响性能

StringBuilder:由于StringBuilder是可变的,它使用可变的字符数组存储字符串,不会每次都创建新的对象。在需要频繁进行字符串拼接、替换等操作时,使用StringBuilder可以提高性能

StringBuffer:与StringBuilder类似,StringBuffer也是可变的,但是它是线程安全的。在多线程环境下,由于同步机制的存在,StringBuffer的性能可能会稍低于StringBuilder

使用场景

String:适用于不需要频繁修改字符串内容的场景。由于String是不可变的,每次对String进行拼接、替换或者修改操作时,都会创建一个新的String对象,这样可能会导致频繁的对象创建和垃圾回收,影响性能。因此,如果字符串内容不需要改变,或者只需要读取字符串的值,可以使用String。

  1. 对于一些常量字符串或者字面量,例如日志输出时的提示信息、固定格式的输出等,由于它们的值是固定的,不需要做任何修改,因此可以使用String。

  2. 在某些业务逻辑中,需要对字符串进行一些操作,但这些操作的结果不会影响原始字符串。例如,从数据库中查询到的数据如果需要展示给用户,通常不会被修改,这种场景下可以使用String。

StringBuilder:适用于单线程环境下需要频繁修改字符串的场景。由于StringBuilder是可变的,它使用可变的字符数组存储字符串,不会每次都创建新的对象。因此,在需要频繁进行字符串拼接、替换等操作时,使用StringBuilder可以提高性能。

  1. 在开发Web应用时,需要将多个字符串拼接成一个完整的HTML页面,这种场景下可以使用StringBuilder。

  2. 当需要从文件中读取数据,并进行一系列复杂的字符串操作时,可以使用StringBuilder。

StringBuffer:适用于多线程环境下需要频繁修改字符串的场景。与StringBuilder类似,StringBuffer也是可变的,但是它是线程安全的,内部的方法都使用了synchronized关键字进行同步。这意味着在多线程环境下,多个线程可以安全地同时访问和修改StringBuffer对象。因此,如果在多线程环境下需要频繁进行字符串操作,应该使用StringBuffer来确保线程安全。

  1. Web服务器需要同时处理多个客户端请求,这种场景下可以使用StringBuffer来确保线程安全。

  2. 一个多线程的任务,需要将多个线程的执行结果拼接成一个完整的字符串,可以使用StringBuffer。

文章来源:https://blog.csdn.net/qq_62262918/article/details/135295786
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。