【JavaSE】String,StringBuilder 和 StringBuffer 的区别

》》》我的博客主页
》》》我的gitee链接
关注我,在学习Java的道路上共同进步!!!

Alt



String 类

String 类是 类型,通过 String 对象的引用 表示和操作 字符串类型 的数据。
由双引号 " " 引起来的内容就是一个字符串常量,Java 编译器会自动将 字符串常量 转换为 String 对象进行处理。
下面是常见的 初始化 String 对象的 3种方式 (效果相同,日常开发推荐第一种方法):

// 方法1:使用常量字符串构造s1对象
String s1 = "hello";
System.out.println(s1);
// 方法2:直接newString一个s2对象
String s2 = new String("hello");
System.out.println(s2);
// 方法3:使用字符数组构造s3对象
char[] array = {'h', 'e', 'l', 'l', 'o'};
String s3 = new String(array);
System.out.println(s3);

String 的不可变性

我们所有对字符串的操作都是 产生新的字符串对象 而不是对 原来字符串对象的基础上进行替换或修改

根本原因:String类中的 value 引用是被 private 修饰,在其他类中根本无法直接获取到这个对象的引用,所有的字符串都是存到字符数组里面的,而字符数组就是引用类型。因此,我们对一个字符串的所有改动都会产生一个新的对象,原来的字符串对象保持不变。
Alt

那 String 不可变的原因为什么不是被 final 修饰的呢?
final 修饰类表明该类不想被继承,final 修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的。因此,如果只是被 final 修饰的 value 这个引用不能改变他所指向对象的引用,但是可以直接对对象进行修改。

我们通过调试下面这段代码证明 String 的不可变性:

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

Alt
Alt
“abc” 拼接完之后, value 中的引用发生了改变,说明 value 的引用所指向的对象已经不同于原来的 “hello” 字符串对象了。

观察字符串修改时的反汇编代码

比如还是下面这段代码:对一个字符串对象拼接另一个字符串

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

运行这个代码之后,然后找到该类的 .class文件所在的路径,通过终端查看该代码的反汇编,查看反汇编的目的是查看编译器是如何解读和处理这段代码的。
Alt
Alt
Alt

我们可以从反汇编代码中发现,它用到了一个 StringBuilder 的类,我们可以对照这个反汇编的注释重新对照着写这7步的 java代码:
Alt
最后发现两个代码的效果是一样的。
Alt

StringBuilder 的可变性(StringBuilder 相较于 String 的优势)

尽量避免直接对 String类型对象 进行修改,因为 String类是不能修改的,所有的修改都会创建新对象,效率非常低下。所以因为这个原因,编译器会优化我之前写的对 String类型对象 进行修改的低效率代码 。通过 StringBuilder 进行字符串修改时不会创建新的对象,只需在原来的 StringBuilder 对象的基础上进行修改即可,这就是 StringBuilder 相较于 String 的优势。
Alt

Alt

我们可以运行一段代码直观的感受 String 与 StringBuilder ,StringBuffer 中效率的差距:

    public static void main(String[] args) {
        //计算 通过10000次循环修改String对象的时间(毫秒)
        long start = System.currentTimeMillis();
        String s = "";
        for(int i = 0; i < 10000; ++i){
            s += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);

        //计算 通过10000次循环修改StringBuffer对象的时间(毫秒)
        start = System.currentTimeMillis();
        StringBuffer sbf = new StringBuffer("");
        for(int i = 0; i < 10000; ++i){
            sbf.append(i);
        }
        end = System.currentTimeMillis();
        System.out.println(end - start);

        //计算 通过10000次循环修改StringBuilder对象的时间(毫秒)
        start = System.currentTimeMillis();
        StringBuilder sbd = new StringBuilder();
        for(int i = 0; i < 10000; ++i){
            sbd.append(i);
        }
        end = System.currentTimeMillis();
        System.out.println(end - start);
    }

StringBuilder 相较于 String 的优点:

在循环的情况下,字符串进行拼接,每次循环 String 都需要新建对象和销毁对象,而 StringBuilder 直接在该对象进行 append

下图是代码的运行结果:
Alt

StringBuilder 与 StringBuffer 的区别

  • StringBuffer 是线程安全的类,也就是只能有一个线程同时调用 StringBuffer 中的方法,其他线程不能同时调用;
  • StringBuilder 不是线程安全的类,所以可以有多个线程同时调用 StringBuilder 中的方法。

但 StringBuffer 每次上锁和解锁都需要耗费资源,在多线程的情况下,虽然很安全,但是效率比StringBuilder 低一些。

总结

  1. String是不可变的类,因为类型对象的字符串不能在原来对象的基础上进行修改,而是会产生一个新的对象;

  2. StringBuilder 和 StringBuffer 是可变的类,因为StringBuilder 和 StringBuffer类型对象的字符串可以直接在原来对象的基础上进行修改;

  3. StringBuffer 在多线程的情况下更安全,StringBuilder 性能更高。

  4. String,StringBuilder 和 StringBuffer 的应用场景:
    – 如果在 单线程环境 下需要 频繁修改字符串,应该使用 StringBuilder;
    – 如果在 多线程环境 下需要 保证线程安全,可以使用 StringBuffer;
    – 如果 字符串 不需要 被修改,则可以使用 String。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值