String、StringBuilder、StringBuffer比较

String为什么是不可变的?

大家在很多文章中看到String底层是一个使用 final 修饰的char数组,所以String是不可变的。其实这个说法什错误的。

先来说明被final修饰会发生什么

修饰的类无法被继承,修饰的方法不能被重写,修改的变量是数据基本类型时值无法被更改,修饰的变量是引用类型时不能在指向其他对象

因此final修饰的char数据并不是String不可变的原因(引用一直指向这个char数组,但是我们可以修改char数组里的值)

提供简单的测试代码,大家可以运行测试

public class Main {
    public static void main(String[] args) {
        final char[] chars = new char[]{'a','b','c'};
        System.out.println(chars); // abc
        chars[1] = 'd';
        System.out.println(chars); // adc
    }
}

String真正不可变的原因:

  1. 保存的char数组被final修饰且私有,并且 String 类没有提供修改这个char数组的方法
  2. String类被final修饰,无法被继承,进而避免了子类破坏 String类

String实例化方法

String有2中实例化方法:

  1. 直接使用 "" 来创建。
  2. 使用 new 关键子来实例化

区别:

  • 使用双引号来创建一个字符串时,会先在字符串常量池中寻找,找到则将字符串池中的字符串对象引用返回;否则,在字符串池中创建字符串对象并返回引用。
  • 使用new关键字来创建字符串对象,每new一个对象都会在堆上申请一个内存空间用来保存字符串对象。

从这个实例化方法中,我们也可以知道字符串使用 == 比较时,为什么有时相同有时不同。

public class Main {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        String d = new String("abc");
        // a与b属于字符串常量池中的同一对象
        System.out.println(a==b);// true
        // a与c一个在字符串常量池中一个在堆上
        System.out.println(a==c);// false
        // c与d都在堆上,但是属于不同空间
        System.out.println(c==d);// false
    }
}

三者比较

  1. String对象是长度不可变的,底层使用了final来修饰char数组来保存字符串
  2. StringBuilder、StringBuffer有公共父类AbstractStringBuilder,它的char数组长度是可变的
  3. 也就是说字符串改变时,其实String是创建了一个新的对象来保存值,而StringBuilder和StringBuffer是在原有的数组上做改动,这使得StringBuilder和StringBuffer的效率在字符串频繁改变时效率比String高
  4. StringBuilder是线程不安全的,StringBuffer是线程安全的,所以StringBuilder比StringBuffer高

字符串拼接

使用String字符串做拼接

String[] arr = {"he", "llo", "world"};
String s = "";
for (int i = 0; i < arr.length; i++) {
    s += arr[i];
}
System.out.println(s);

字节码文件:

从字节码文件可以看出String中使用“+”进行字符串拼接实际上调用的是StringBuilder的append方法。意味着每次进行字符串拼接都会创建一次StringBuilder对象。

使用StringBuilder进行字符串拼接

String[] arr = {"he", "llo", "world"};
StringBuilder s = new StringBuilder();
for (String value : arr) {
    s.append(value);
}
System.out.println(s);

字节码文件:

如果使用StringBuilder的append方法进行拼接,可以节省大量创建对象的时间,这也是StringBuilder比String快的主要原因。

效率高低

StringBuilder > StringBuffer > String

JDK9优化

在JDK9之后String做了优化,使用 “+” 做字符串拼接时,会调用动态指令,性能比StringBuilder要高,原因是StringBuilder在开启字符串压缩的情况下,无法压缩的字符串(如中文)会多进行一个内存的分配,而String会默认计算字符串的长度,只需要分配一次内存。

StringBuilder字符串压缩

  1. 在加入字符串时,把所有字符串都当做无法压缩处理
  2. 在进行toString()时,先当做可以压缩来处理,以能够压缩为前提分配一次内存,尝试进行一次复制;如果最后发现不能压缩,那么以没有压缩的内存量再分配一次

如果大家想详细了解这部分内容可以阅读这篇文章

来源: 还在无脑用 StringBuilder?来重温一下字符串拼接吧 - 掘金

  • 48
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值