欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

String、StringBuilder和StringBuffer的区别

程序员文章站 2022-05-03 23:37:15
String、StringBuilder和StringBuffer的区别1. 可变性String 不可变StringBuffer 和 StringBuilder 可变2. 线程安全String 不可变,因此是线程安全的StringBuilder 不是线程安全的StringBuffer 是线程安全的,内部使用 synchronized 进行同步String查看String类的继承关系:public final class String implements java.io....

String、StringBuilder和StringBuffer的区别

1. 可变性

  • String 不可变
  • StringBuffer 和 StringBuilder 可变

2. 线程安全

  • String 不可变,因此是线程安全的
  • StringBuilder 不是线程安全的
  • StringBuffer 是线程安全的,内部使用 synchronized 进行同步

String

查看String类的继承关系:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence 

final修饰的class是不可以被继承的。

其次String内部存储字符串的结构是char数组,而且是final修饰的,因此在进行字符串的拼接时效率较低。

每次对字符串修改,底层都会重新开辟新的堆内存空间,这样会开辟很多个空间地址,造成浪费。

 /** The value is used for character storage. */
    private final char value[];

分析String对象用+进行字符串拼接时的底层执行过程:

首先写一个简单的java文件:

public class Test{

	public static void main(String[] args){
		String a = "a";
		String b = a+"b";
		System.out.println(b);
	}

}

然后用javap -verbose Test进行反编译,得到以下内容:

Last modified 2020-11-8; size 585 bytes
  MD5 checksum a00e5acaccc9ac98c689faae92229e7d
  Compiled from "Test.java"
public class Test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #11.#20        // java/lang/Object."<init>":()V
   #2 = String             #21            // a
   #3 = Class              #22            // java/lang/StringBuilder
   #4 = Methodref          #3.#20         // java/lang/StringBuilder."<init>":()V
   #5 = Methodref          #3.#23         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #6 = String             #24            // b
   #7 = Methodref          #3.#25         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #8 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
   #9 = Methodref          #28.#29        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #10 = Class              #30            // Test
  #11 = Class              #31            // java/lang/Object
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               main
  #17 = Utf8               ([Ljava/lang/String;)V
  #18 = Utf8               SourceFile
  #19 = Utf8               Test.java
  #20 = NameAndType        #12:#13        // "<init>":()V
  #21 = Utf8               a
  #22 = Utf8               java/lang/StringBuilder
  #23 = NameAndType        #32:#33        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #24 = Utf8               b
  #25 = NameAndType        #34:#35        // toString:()Ljava/lang/String;
  #26 = Class              #36            // java/lang/System
  #27 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
  #28 = Class              #39            // java/io/PrintStream
  #29 = NameAndType        #40:#41        // println:(Ljava/lang/String;)V
  #30 = Utf8               Test
  #31 = Utf8               java/lang/Object
  #32 = Utf8               append
  #33 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #34 = Utf8               toString
  #35 = Utf8               ()Ljava/lang/String;
  #36 = Utf8               java/lang/System
  #37 = Utf8               out
  #38 = Utf8               Ljava/io/PrintStream;
  #39 = Utf8               java/io/PrintStream
  #40 = Utf8               println
  #41 = Utf8               (Ljava/lang/String;)V
{
  public Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        10: aload_1
        11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        14: ldc           #6                  // String b
        16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: astore_2
        23: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        26: aload_2
        27: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        30: return
      LineNumberTable:
        line 4: 0
        line 5: 3
        line 6: 23
        line 7: 30
}
SourceFile: "Test.java"

会发现底层还是调用了StringBuilder的append()方法。

StringBuilder

查看StringBuilder类的继承关系:

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

存储字符串的底层结构

 /**
 * The value is used for character storage.
 */
char[] value;

StringBuffer

查看StringBuffer类的继承关系:

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

发现StringBuffer与StringBuilder继承的是同一个抽象类

StringBuffer是一个字符串缓冲区,如果需要频繁的对字符串进行拼接时,建议使用StringBuffer。

StringBuffer的底层是char数组,与StringBuilder一样,继承自AbstractStringBuilder

 /**
 * The value is used for character storage.
 */
char[] value;

在使用时如果数组容量不够了,则会通过数组的拷贝对数组进行扩容,所以在使用StringBuffer时最好预测并手动初始化长度,这样能够减少数组的拷贝,从而提高效率。

StringBuilder与StringBuffer的方法基本一致,最大的区别是StringBuffer的普通方法都有synchronized关键字,也就是线程安全的。

总结

如果需要对字符串进行频繁拼接的话,建议使用StringBuffer或者StringBuilder,两者的使用方法一致,下面以StringBuffer为例说明。

拼接字符串的四种方法:
String类的:+、concat()方法

​StringBuilder类:append()方法

SringBuffer类:append()方法

测试这四种方法的效率:

/**
 * 字符串拼接的各种方式性能对比
 */
public class StringConcatTest {
    public static void testAdd(int num){
        long start = System.currentTimeMillis();
        String str = "";
        for(int i = 0; i < num; i++){
            str += i;
        }
        System.out.println("字符串拼接使用 + 耗时:" + (System.currentTimeMillis() - start) + "ms");
    }

    public static void testConcat(int num){
        long start = System.currentTimeMillis();
        String str = "";
        for(int i = 0; i < num; i++){
            str.concat(String.valueOf(i));
        }
        System.out.println("字符串拼接使用 concat 耗时:" + (System.currentTimeMillis() - start) + "ms");
    }

    public static void testStringBuffer(int num){
        long start = System.currentTimeMillis();
        StringBuffer stringBuffer = new StringBuffer();
        for(int i = 0; i < num; i++){
            stringBuffer.append(String.valueOf(i));
        }
        stringBuffer.toString();
        System.out.println("字符串拼接使用 StringBuffer 耗时:" + (System.currentTimeMillis() - start) + "ms");
    }

    public static void testStringBuilder(int num){
        long start = System.currentTimeMillis();
        StringBuilder stringBuilder = new StringBuilder();
        for(int i = 0; i < num; i++){
            stringBuilder.append(String.valueOf(i));
        }
        stringBuilder.toString();
        System.out.println("字符串拼接使用 StringBuilder 耗时:" + (System.currentTimeMillis() - start) + "ms");
    }

    public static void main(String[] args) {
        int num = 100000;
        //+号拼接
        testAdd(num);
        //concat方法拼接
        testConcat(num);
        //StringBuffer的append方法拼接
        testStringBuffer(num);
        //StringBuilder的append方法拼接
        testStringBuilder(num);
    }
}

执行结果如下:

字符串拼接使用 + 耗时:18466ms
字符串拼接使用 concat 耗时:11ms
字符串拼接使用 StringBuffer 耗时:6ms
字符串拼接使用 StringBuilder 耗时:6ms

Process finished with exit code 0

所以在需要进行大量的字符串拼接时应该尽量使用StringBuffer和StringBuilder,具体场景看是否需要线程安全。

本文地址:https://blog.csdn.net/weixin_43260413/article/details/109564381

相关标签: 基础 java