String、StringBuilder、StringBuffer的区别(超详细)

string、stringBuffer、stringBuilder区别

string、stringBuffer、stringBuilder

创建一个String对象
public class StringTest1 {
    public static void main(String[] args) {
    	//通过构造放方法,将一个字符串封装为一个String对象
        String str1 = new String("hello world");
        
        //通过直接赋值创建一个String对象
        String str2 = "hello world";
        
        //声明一个char[]数组
        char[] ch1 = {'h','e','l','l','o',' ','w','o','r','l,'d
        
        //将char[]数组封装为一个String对象
        Stirng str3 = new String(ch1);
        
        //指定开始结束索引,将char[]数组封装为一个String对象
        String str4 = new String(ch1,2,4);
        
    }
}
String对象的定义
public final class String
	implements java.io.Serializable, Comparable<String>, CharSequence {
		//.....
	}

通过查看String类,可以看出String类实现了三个接口

实现Serializable接口:实现这个接口意味着String对象可以被序列化(序列化可以实现对象的保存与网络传输)

实现Comparable接口:这个接口用于定义一个可比较大小的类型,实现这个接口意味着我们可以重写其中的compareTo()方法来定义两个对象之间的顺序关系。

实现CharSequence接口:StringBulider作为实现CharSequence接口的三大类之一,实现CharSequence接口StringBuilder类可以被视为一个可读、可修改并且长度可变的字符序列。这意味着我们可以通过StringBuilder对象进行字符串拼接、插入字符、替换部分文本或删除特定区间内的内容等操作。因此,在使用StringBuilder时,我们可以像操作普通字符串一样方便地对其进行增删改查操作,并且避免频繁创建新对象带来的开销。

值得注意的是:

String类中value字段用于储存字符串的实际内容,可以看见,value是一个char[]数组,确定长度后不可改变,并且用final修饰,这就表示value的引用地址是不可变的(但里面的元素是可变的),再加上final修饰的String类不能被继承,这就决定value数组的元素值已经无法从外部修改了,这决定了String对象是不可变的。
在这里插入图片描述

String中的部分常见方法
方法说明
length();返回字符串长度
charAt(int index);返回指定索引的字符
concat(String str);字符串的拼接
equalsIgnoreCase(String anotherString);比较字符串是否相等,忽略大小写
indexOf(int ch)和indexOf(String str, int fromIndex);查找指定字符(或子串)第一次出现的位置
substring(int beginIndex)和substring(int beginIndex, int endIndex);获取指定位置的字串
toLowerCase();和toUpperCase();转换大小写
trim();去除字符串首尾空格
replace(char oldChar, char newChar);字符替换
split(String regex);以特定正则表达式分隔符将当前字符创拆分为数组
startsWith(String prefix)和endsWith(String suffix)判断给定前缀或者给定后缀是否匹配
equals(Object anObject);两个字符串的比较
compareTo(String anotherString);比较两个字符串是否相等,并返回结果

StringBuilder

创建一个StringBuilder对象:
public class StringTest1 {
    public static void main(String[] args) {
        //创建一个空的可变字符串对象
        StringBuilder strb1 = new StringBuilder();
        System.out.println(strb1);
        
        //创建一个可变字符串对象
        StringBuilder strb2 = new StringBuilder("hello world");
        System.out.println(strb2);
    }
}
StringBuilder对象的定义
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
	//..........
}

通过查看java中的StringBuilder类,可以看出它继承了AbstractStringBuilder类,实现了Serializable接口与CharSequence接口

继承了AbstractStringBuilder类:一个抽象类,提供了构建字符串所需要的基本功能和方法

实现了Serializable接口:实现这个接口意味着StringBuilder对象可以被序列化(序列化可以实现对象的保存与网络传输)

实现CharSequence接口:StringBulider作为实现CharSequence接口的三大类之一,实现CharSequence接口StringBuilder类可以被视为一个可读、可修改并且长度可变的字符序列。这意味着我们可以通过StringBuilder对象进行字符串拼接、插入字符、替换部分文本或删除特定区间内的内容等操作。因此,在使用StringBuilder时,我们可以像操作普通字符串一样方便地对其进行增删改查操作,并且避免频繁创建新对象带来的开销。

StringBuilder中的部分常见方法:
方法功能
append(任意类型参数);将指定的类型参数添加到字符串构建器的末尾
insert();从指定位置插入指定类型的数据
delete();/deleteCharAt();从字符串构建器中删除字符或字符序列
indexOf(设置参数);返回指定字符或子串的索引值
substring();返回范围内子串作为新的字符串对象
reverse();将字符串反转

StringBulider中的append()添加方法:

	strb2.append(任意类型参数);

在这里插入图片描述

在StringBuilder类中有各种类型的参数的添加方法,实现了StringBuilder的扩展功能

底层实现代码(节选):

在这里插入图片描述

StringBuilder中的insert()插入方法:

	//传递两个参数1、offset(插入位置索引),2、xxx(任意类型数据)
	str1.insert(int offset,Xxx xxx);

在这里插入图片描述

insert方法实现了StringBuilder对象指定位置插入数据功能

底层实现代码(节选):

在这里插入图片描述

StringBulider中的delete()/deleteCharAt()删除方法:

	//传递两个参数1、删除开始位置索引2、删除结束位置索引
	strb1.delete(int start,int end);
	//传递一个参数1、删除指定位置索引
	strb1.delete(int offset);

在这里插入图片描述

delete()/deleteCharAt()方法实现了StringBuilder对象内容的删除操作

底层实现代码:
在这里插入图片描述

StringBulider中的indexOf()查找方法:

	//返回strb1中第一次出现指定字符串的索引位置
	strb1.indexOf(String str);
	//从给定索引位置fromIndex开始,返回搜索过程中第一次出现指定字符或字符串的索引位置
	strb1.indexOf(String str,int fromIndex);
	//返回strb1中第一次出现指定字符的索引位置
	strb1.indexOf(char ch)
	//从给定索引位置fromIndex开始,返回搜索过程中第一次出现指定字符或字符的索引位置
	strb1.indexOf(char ch,int fromIndex)

image-20230629230940311

底层实现代码:

在这里插入图片描述

StringBulider中的substring();返回指定字符串方法:

	//返回指定索引开始后的字符串
	strb1.substring(int start);
	//返回指定开始结束索引的字符串
	strb1.substring(int start,int end);

在这里插入图片描述

底层实现代码:

在这里插入图片描述

StringBulider中的reverse();反转字符串方法:

	//将指定字符串内容反转
	strb1.reverse();

在这里插入图片描述

底层实现代码:
在这里插入图片描述

在这里插入图片描述

String与StringBuilder转换
方法功能
String s=strb1.toString();通过toString()方法实现StringBuilder对象转化为String对象
StringBuilder strb3=new StringBuilder(s);通过构造方法实现String对象转换为StringBuilder对象

StringBuffer

创建一个StringBuffer对象
public class StringTest1 {
    public static void main(String[] args) {
    	//创建一个空的StringBuffer对象
        StringBuffer strbf1 = new StringBuffer();
        System.out.println(strbf1);
        
        //创建一个StringBuffer对象
        StringBuffer strbf2 = new StringBuffer("hello world");
        System.out.println(strbf2);
    }
}
StringBuffer对象的定义
public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence{
    	//......
    }

通过查看java中的StringBuffer类,可以看出它继承了AbstractStringBuilder类,实现了Serializable接口与CharSequence接口

继承了AbstractStringBuilder类:一个抽象类,提供了构建字符串所需要的基本功能和方法

实现了Serializable接口:实现这个接口意味着StringBuilder对象可以被序列化(序列化可以实现对象的保存与网络传输)

实现CharSequence接口:StringBulider作为实现CharSequence接口的三大类之一,实现CharSequence接口StringBuilder类可以被视为一个可读、可修改并且长度可变的字符序列。这意味着我们可以通过StringBuilder对象进行字符串拼接、插入字符、替换部分文本或删除特定区间内的内容等操作。因此,在使用StringBuilder时,我们可以像操作普通字符串一样方便地对其进行增删改查操作,并且避免频繁创建新对象带来的开销。

StringBuffer中常见的部分方法
方法说明
append(String str);将指定的字符串追加到字符串缓冲区末尾。
insert(int offset, String str);在指定偏移量位置插入指定字符串。
delete(int start, int end);删除从start索引开始到end索引结束之间的字符序列
replace(int start, int end, String str);指定字符串替换从索引开始到索引结束之间的字符序列。
reverse();将整个字符序列反转
capacity();返回当前容量(即总空间)
length();返回当前长度(即已使用空间)
StringBuffer与String的转换
方法说明
StringBuffer strbf1=new StringBuffer(str);通过构造方法实现对String对象转换为StringBuffe对象
strbf1.toString通过toString方法实现StringBuffe对象转换为String对象

三者的区别

可变性

String类是不可变的,意味着一旦创建了一个String对象,就不能更改其内容。而StringBuffer和StringBuilder类是可变的,可以通过方法进行字符串的修改

原理
String不可变:

String类中value字段用于储存字符串的实际内容,可以看见,value是一个char[]数组,确定长度后不可改变,并且用final修饰,这就表示value的引用地址是不可变的(但里面的元素是可变的),再加上final修饰的String类不能被继承,这就决定value数组的元素值已经无法从外部修改了,这决定了String对象是不可变的。

在这里插入图片描述

存储机制:

直接赋值的方式创建一个字符串,此时字符串存储在方法区的常量池中

	String str1="hello";
	String str2="hello";

在这里插入图片描述

	String str1="hello";
	String str2="hello";
	str2="hi";

在修改字符串的值时,无论是在原有字符串内容上做删减,还是赋值一个新的字符串,都需要分配一个新的内存地址进行赋值

在这里插入图片描述

构造方法的方式创建一个字符串,此时字符串存储在方法区的常量池中,但对象储存在堆中

	String str1="hello";
	String str2=new String("hello");

在这里插入图片描述

	String str1=new String("hello");
	String str2=new String("hello");

在这里插入图片描述

	String str1=new String("hello");
	String str2=new String("hi");

在这里插入图片描述

StringBuffer与StringBuilder可变

StringBuffer与StringBuilder对象他们在创建时,都为其构造了一个16个字符的缓冲区,可对其进行后续的修改

StringBuilder的构造方法

在这里插入图片描述

StringBuffer的构造方法
在这里插入图片描述

修改的过程

以append(char[] str, int offset, int len)底层代码为例

提供添加的字符或字符序列,以索引开始于结束位置进行插入,其中当传入字符或字符序列为空,则赋null,在后续的判断中,若开始索引小于0或大于结束索引,结束索引大于传入字符长度时就抛出异常通过for循环添加传入的非空字段(若传入字符串为空,且开始结束索引为0时,for循环执行0次)

线程安全性

StirngBuffer是线程安全的, 因此适合在多线程环境下使用;而StringBuilder不具备线程安全性,适合在单线程环境下使用。而String没有任何同步机制,并且也不保证线程安全。

StringBuilder导致线程不安全的两个原因

1、输出错误(不满足预期)

我们创建10个线程,每个线程进行对StringBuilder对象进行1000次添加字符‘a’的操作

public static void main(String[] args) throws InterruptedExcept
  StringBuilder stringBuilder = new StringBuilder();
  for (int i = 0; i < 10; i++) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            for (int j = 0; j < 1000; j++) {
                stringBuilder.append("a");
            }
        }
    }).start();
  }
  Thread.sleep(100);
  System.out.println(stringBuilder.length());
}

但我们查看底层代码时发现:

在这里插入图片描述

代码中的count+=len可能会导致出错,举例:当两个线程同时执行到count+=len上一行,他们此时提取的count值都为a,此时第一个线程执行count+=len,此时count值变为a+1,但第二个线程继续执行count+=len时,因为进行了两次添加,理应执行完后count值为a+2。但第二个线程执行时提取的count为a,执行后count值依旧为a+1。会影响到后续线程的添加操作,这就导致总添加的字符数量少了一个,多线程下,会导致更严重的后果。

2、添加字符是索引值出错

在创建StringBuilder时,系统为其构造了一个16个字符的缓冲区,对象的扩展时,就是先向这个缓冲区添加,当传入扩展的字符序列长度大于剩余容量,就会new一个两倍原StringBuilder长度char[]数组,将原StringBuilder对象内容复制到新的char[]数组中。举例:当剩余容量为a,此时两个线程都需插入一个长度为a的字符序列,且他们都执行到提取剩余容量,取得的容量刚好可以符合他们的要求,此时他们继续往下执行,当第一个线程插入字符序列后,容量没有了,但是第二个线程提取到的信息是过时的,它依旧认为还有剩余容量,在进行添加操作时,就会导致ArrayIndexOutOfBoundsException异常

StringBuffer线程安全的原因

对于StringBuffer来说, 它的方法定义为 synchronized ,即采用了互斥锁机制(lock mechanism),能够确保同一时间只有一个线程可以访问该对象进行修改,从而保证了操作串行化(serialized access),任意时刻只能有一个线程执行append、delete等方法。

性能

由于String类每次对字符串进行修改都会创建一个新的对象,在频繁操作大量数据时会产生大量无用对象,导致内存开销较大;相比之下,StingBuffer和StringBuilder直接对原始字符序列进行修改,减少了内存开销。

使用建议

  • 如果需要频繁进行字符串拼接或者修改,并且有多个并发访问该字符串,则应选择使用ThreadSafe等级较高(但效率稍低)的 StringBffer 类。
  • 如果只有单个线程访问该字节串,并且有频繁操作拼接或者修改,则应选择使用 StringBuilder 类。
    ,即采用了互斥锁机制(lock mechanism),能够确保同一时间只有一个线程可以访问该对象进行修改,从而保证了操作串行化(serialized access),任意时刻只能有一个线程执行append、delete等方法。

性能

由于String类每次对字符串进行修改都会创建一个新的对象,在频繁操作大量数据时会产生大量无用对象,导致内存开销较大;相比之下,StingBuffer和StringBuilder直接对原始字符序列进行修改,减少了内存开销。

使用建议

  • 如果需要频繁进行字符串拼接或者修改,并且有多个并发访问该字符串,则应选择使用ThreadSafe等级较高(但效率稍低)的 StringBffer 类。
  • 如果只有单个线程访问该字节串,并且有频繁操作拼接或者修改,则应选择使用 StringBuilder 类。
  • 如果不需要频繁操作拼接或者修改字节串,则可以选则常规(String)方式。
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值