Java基础之String、StringBuffer、StringBuilder的区别?

本文详细介绍了Java中String、StringBuffer和StringBuilder的区别。String是不可变对象,适合少量数据操作;StringBuffer和StringBuilder是可变的,其中StringBuffer线程安全,适用于多线程环境,StringBuilder则没有线程安全措施,但在单线程环境下性能优于StringBuffer。在大量字符串连接操作中,StringBuilder和StringBuffer的效率远超String。在实际使用中,根据需求选择合适的字符串处理类以优化性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:String、StringBuffer和StringBuilder?
首先,从可变性来说,String类中使⽤ final 关键字修饰字符数组来保存字符串,所以String对象是不可变的。⽽ StringBuilder 与 StringBuffer都继承⾃ AbstractStringBuilder类,在 AbstractStringBuilder中也是使⽤字符数组保存字符串,但是没有⽤ final 关键字修饰,所以这两种对象都是可变的。
其次,从线程安全性来说,String中的对象是不可变的,也就可以理解为常量,因此是线程安全的。AbstractStringBuilder 是StringBuilder与StringBuffer 的公共⽗类,定义了⼀些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共⽅法。StringBuffer对⽅法加了同步锁或者对调⽤的⽅法加了同步锁,所以是线程安全的。而StringBuilder并没有对⽅法进⾏加同步锁,是⾮线程安全的。
最后,从性能来说,每次对 String类型进⾏改变的时候,都会⽣成⼀个新的 String对象,然后将指针指向新的String对象。StringBuffer每次都会对 StringBuffer 对象本身进⾏操作,⽽不是⽣成新的对象并改变对象引⽤。相同情况下使⽤ StringBuilder相⽐使⽤ StringBuffer仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的⻛险。
总结的来说,我们在操作少量数据的时候推荐使用 String,单线程操作字符串缓冲区下操作⼤量数据的时候推荐使用 StringBuilder,多线程操作字符串缓冲区下在操作⼤量数据的时候推荐使用 StringBuffer。

1.String

1.1 String内存存储

存储方式:在字符串的内部使用char数组来保存字符串的内容,数据中的每一位存放一个字符,char数组的长度也就是字符串长度,例如:“hello world”的存储方式为:
在这里插入图片描述
在源码中的表示为:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];//因为有“final”修饰符,所以可以知道string对象是不可变的。
    /** Cache the hash code for the string */
    private int hash; // Default to 0
}

String内存位置的说明:显式的(在编译的时候可以确定的字符串)是存放在字符串常量池中,而new出来的String对象是存放在中的,字符串对象的引用是存放在中的,即使用了new关键字或者是编译的时候不确定的值则不会使用字符串池。
◣注意:在JDK1.7之前,字符串常量池是在方法区中,在JDK1.7之后字符串常量池被移到堆中了。
(1)显式的String常量

    String s1="hello";
	String s3="hello";
    System.out.println(s1==s3);//true

说明:第一句代码执行后就在常量池中创建了一个值为hello的String对象,并将该内存地址赋值给s1;第二句执行时,因为常量池中存在hello所以就不再创建新的String对象了,直接将s1的内存地址返回给s3。此时该字符串的引用s1和s3在虚拟机栈里面,但是”hello”在常量池中,所以他们用“==”比较的结果为true。

(2)String对象

    String s2=new String("hello");
	String s4=new String("hello");
    System.out.println(s2==s4);//false

说明:Class被加载时就在常量池中创建了一个值为hello的String对象,第一句执行时会在堆里创建new String(“hello”)对象,因为通过new方法创建对象是直接在堆内存中分配空间用来创建字符串的;第二句执行时,因为常量池中存在hello所以就不再创建新的String对象了,直接在堆里创建new String(“hello”)对象;所以s2和s4用“==”比较的时候默认比较的是堆中的地址,所以返回false。

◣注意:两个字符串比较的时候使用equals比较才会比较字符串的内容(其他的引用类型同理)。

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        //instanceof 是 Java 中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回 true;否则,返回 false。
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

1.2 String类中常用方法

返回类型方法名称作用
intlength()获取字符串的长度
charcharAt(int i)返回指定索引处的 char 值,即获取字符串中的某一个字符
intindexOf(String s)返回传入字符串/字符在原来字符串中第一次出现的位置,如果没有找到则返回-1
intindexOf(String s,int from index)返回在此字符串/字符中第一次出现指定字符处的索引,从指定的索引开始搜索
intlastIndexOf(String s)返回传入字符串在原来字符串中最后一次出现的位置,如果没有找到返回-1
booleanstartWith(String s)判断字符串是否是传入的字符串开头
booleanendWith(String s)判断字符串是否是传入字符串结尾
StringtoLowerCase()将字符串转换成小写字符串
StringtoUpperCase()将字符串转换成大写字符串
StringsubString(int start)截取字符串,从传入的位置截取到末尾
StringsubString(int start,int end)截取字符串,从传入的参数1的位置开始,到参数2的位置结束。
Stringtrim()去掉字符串两边的空格(中间的去不掉)
String[]split(String s)将源字符串按照传入的参数分割成字符串数组
stringreplace(new char,old char)返回一个新的字符串,它是通过用 new char 替换此字符串中出现的所有 old char 得到的

2.StringBuffer

String类是一个常量类,如果想创建一个内容可变的字符串对象使用String类就不行了,所以java提供了一个StringBuffer类。

public final class StringBuffer extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{/**
     * A cache of the last value returned by toString. Cleared
     * whenever the StringBuffer is modified.
     */
    private transient char[] toStringCache;
    }

StringBuffer类是一个长度可变的字符串,它包括三大操作字符串的方法:
(1)构建字符串:可以使用append方法向末尾加入字符串,也可以使用insert方法向字符串中插入字符串,同时我们也可以在创建StringBuffer对象时设置一个初始字符串。例如:StringBuffer sbu=new StringBuffer(“hello”);
(2)获取字符串:可以使用subString获取当前字符串的一部分,也可以使用toString方法获得所有的字符串。
(3)修改字符串:可以使用replace方法替换字符串中的部分内容,也可以使用delete方法来删除字符串中的某些字符。
◣注意:具体的方法可以查看java API

public String(StringBuffer buffer) {
        synchronized(buffer) {//StringBuffer是线程安全的
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
}

3.StringBuilder

StringBulider是从JDK1.5之后加入进来的,这个类与StringBuffer的使用方式一样,不过他们的不同之处就在于StringBuffer类中的大部分方法都是线程安全的,而StringBulider不是线程安全的。
◣ 注意:对于这两个操作字符串来说,使用哪个可以自己去决定,不过在处理多线程的时候使用StringBuffer比较安全。

public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

4.String/StringBuffer/StringBuilder的比较

(1)可变与不可变
String类中使用字符数组保存字符串,因为有“final”修饰符,所以string对象是不可变的。
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,但是这两种对象都是可变的。
(2)多线程安全
String中的对象是不可变的,也就可以理解为常量,显然线程安全。
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
(3)StringBuilder与StringBuffer共同点
StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。
抽象类与接口的其中一个区别是:抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的申明和常量的定义。
(4)字符串连接
当进行大量字符串连接的时候,一般使用StringBuffer或StringBulider,因为他提供了一个缓冲区的功能,在字符串拼接的时候不会去频繁的操作内存,而使用加号进行字符串拼接的时候会频繁的操作内存,这样导致程序性能降低。
例如:

public class Test{
	/**
	 * StringBuffer:一个类似于 String 的字符串缓冲区,线程安全的,重量级的,大量进行字符串拼接的操作效率高于String
	 * StringBulider:JDK1.5之后出现的,非线程安全的,轻量级的
	 * 大量进行字符串拼接的效率:StringBulider > StringBuffer > String
	 * 建议:如果要进行大量的字符串拼接,要使用StringBulider
	 */
	public static void main(String[] args) {
		Season sea=Season.SPRING;
		Season1 sea1=Season1.SPRING;
		//Season sea=new Season("春天");
		String s="";
		long start=System.currentTimeMillis();
		for(int i=0;i<100000;i++){
			s=s+i;
		}
		long end=System.currentTimeMillis();
		
		//System.out.println(s);
		//System.out.println("String使用的时间为"+(end-start));
		StringBuffer sbu=new StringBuffer();
		long start1=System.currentTimeMillis();
		for(int i=0;i<100000;i++){
			//s=s+i;
			sbu.append(i);//进行字符串的拼接的方法
		}
		long end1=System.currentTimeMillis();

		//System.out.println(sbu.toString());
		System.out.println("StringBuffer使用的时间为"+(end1-start1));
		StringBuilder sbu1=new StringBuilder();
		long start2=System.currentTimeMillis();
		for(int i=0;i<100000;i++){
			//s=s+i;
			sbu1.append(i);//进行字符串的拼接的方法
		}
		long end2=System.currentTimeMillis();

		//System.out.println(sbu.toString());
		System.out.println("StringBuilder使用的时间为"+(end2-start2));
	
	}
}

运行结果:
在这里插入图片描述
从以上的运行结果可以看出:
大量进行字符串拼接的效率:StringBulider > StringBuffer > String

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值