【每日一读】String , StringBuffer 和 StringBuilder

本文介绍了Java中String、StringBuffer和StringBuilder的区别。String是不可变的,适合字符串常量;StringBuffer线程安全,适合多线程操作;StringBuilder效率更高但线程不安全。JVM预热后,StringBuffer在某些场景下比StringBuilder快。高频面试题涉及到字符串拼接、对象创建以及线程安全等知识点。

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

String 对象一旦创建,值是不能修改的。
String底层是用数组来存值的。如果需要频繁修改字符串,则使用StringBuffer。
String 和 StringBuffer的耗时

public static void main(String[] args) {
        long st = System.currentTimeMillis();
        String str = "";
        for(int i=0;i<50000;i++){
            str += i;
        }
        long et = System.currentTimeMillis();
        System.out.println(et-st);

        long st2 = System.currentTimeMillis();
        StringBuffer sb = new StringBuffer();
        for(int j=0;j<50000;j++){
            sb.append(j);
        }
        long et2 = System.currentTimeMillis();
        System.out.println(et2-st2);
    }

结果:String -> 1807,StringBuffer -> 6.

StringBuffer 初始默认长度16。
当我们创建一个StringBuffer对象时,长度为super(str.length()+16),创建了一个字符串缓冲区。

StringBuilder 和 StringBuffer 是一对兄弟,因为它们拥有同一个父类 AbstractStringBuilder,同时实现的接口也是完全一样,都实现了 java.io.Serializable, CharSequence 两个接口。

区别
StringBuffer 添加了 synchronized 关键字修饰(线程安全),而 StringBuilder 没有。
因为StringBuffer是线程安全的,所以效率通没有StringBuilder高。

比较StringBuffer 和 StringBuffer的耗时

public static void main(String[] args) {
        for(int i=0;i<100000;i++){
            test t =new test();
        }
        System.out.println("⬆️⬆️⬆️⬆️⬆️ warm up code ⬆️⬆️⬆️⬆️⬆️");
        System.out.println("===============================");
        long st = System.currentTimeMillis();
        StringBuilder s1 = new StringBuilder();
        for(int i=0;i<500000;i++){
            s1.append(i);
        }
        long et = System.currentTimeMillis();
        System.out.println("StringBuilder run Times: "+(et-st));
        System.out.println("===============================");
        long st2 = System.currentTimeMillis();
        StringBuffer s2 = new StringBuffer();
        for(int j=0;j<500000;j++){
            s2.append(j);
        }
        long et2 = System.currentTimeMillis();
        System.out.println("StringBuffer run Times: "+(et2-st2));

    }

结果:

⬆️⬆️⬆️⬆️⬆️ warm up code ⬆️⬆️⬆️⬆️⬆️
===============================
StringBuilder run Times: 39
===============================
StringBuffer run Times: 52

在比较两者效率的时候,最开始发现StringBuffer有时会比StringBuilder运行得快。后来发现这里有一个概念叫虚拟机预热(warm up the JVM)

JVM 预热是指什么?
类加载过程完毕后,所有需要的类会进入 JVM cache (native code) ,这样就可以被快速的实时访问。
当应用的第一个请求到来,会触发逻辑相关类的第一次加载,此过程会有一定的耗时,会影响第一次调用的实时响应。这主要是因为JVM的懒加载及JIT机制。因此对于低延迟应用,必须采用特定的策略来处理第一次的预加载逻辑,以保障第一次的请求的快速响应。此过程,我们称之为 JVM 的预热。

所以当我预热JVM之后,得到的结果就相对比较准确了。

StringBuilder为什么线程不安全?

public static void main(String[] args) {

        StringBuilder s1 = new StringBuilder();
        for(int i=0;i<100;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                   for(int j=0;j<1000;j++){
                       s1.append("a");
                   }
                }
            }).start();
        }

        try {
            Thread.sleep(100);
            System.out.println(s1.length());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

当我们开启100个线程的时候,发现结果98642比100000小。


高频面试题

1、StringBuilder 的效率一定比 String 更高吗?

我们通常会说 StringBuilder 效率要比 String 高,严谨一点这句话不完全对,虽然大部分情况下使用 StringBuilder 效率更高,但在某些特定情况下不一定是这样,比如下面这段代码:

String str = "Hello"+"World";
StringBuilder stringBuilder = new StringBuilder("Hello");
stringBuilder.append("World");

此时,使用 String 创建 “HelloWorld” 的效率要高于使用 StringBuilder 创建 “HelloWorld”,这是为什么呢?

因为 String 对象的直接相加,JVM 会自动对其进行优化,也就是说 “Hello”+“World” 在编译期间会自动优化为 “HelloWorld”,直接一次性创建完成,所以效率肯定要高于 StringBuffer 的 append 拼接。

但是需要注意的是如果是这样的代码:

String str1 = "Hello";
String str2 = "World";
String str3 = str1+str2;

对于这种间接相加的操作,效率要比直接相加低,因为在编译器不会对引用变量进行优化。

2、下面代码的运行结果是?

String str1 = "Hello World";
String str2 = "Hello"+" World";
System.out.println(str1 == str2);

true,因为 “Hello”+" World" 在编译期间会被 JVM 自动优化成 “Hello World”,是一个字符串常量,所以和 str1 引用相同。

3、下面代码的运行结果是?

String str1 = "Hello World";
String str2 = "Hello";
String str3 = str2 + " World";
System.out.println(str1 == str3);

false,JVM 只有在 String 对象直接拼接的时候才会进行优化,如果是对变量进行拼接则不会优化,所以 str2 + " World" 并不会直接优化成字符串常量 “Hello World”,同时这种间接拼接的结果是存放在堆内存中的,所以 str1 和 str3 的引用肯定不同。

4、String str = new String(“Hello World”) 创建了几个对象?

这是很常见的一道面试题,大部分的答案都是 2 个,“Hello World” 是一个,另一个是指向字符串的变量 str,其实是不准确的。

因为代码的执行过程和类的加载过程是有区别的,如果只看运行期间,这段代码只创建了 1 个对象,new 只调用了一次,即在堆上创建的 “Hello World” 对象。

而在类加载的过程中,创建了 2 个对象,一个是字符串字面量 “Hello World” 在字符串常量池中所对应的实例,另一个是通过 new String(“Hello World”) 在堆中创建并初始化,内容与 “Hello World” 相同的实例。

所以在回答这道题的时候,可以先问清楚面试官是在代码执行过程中,还是在类加载过程中。这道题目如果换做是 String str = new String(“Hello World”) 涉及到几个对象,那么答案就是 2 个。

5、String、StringBuffer、StringBuilder 有什么区别?

1、String 一旦创建不可变,如果修改即创建新的对象,StringBuffer 和 StringBuilder 可变,修改之后引用不变。

2、String 对象直接拼接效率高,但是如果执行的是间接拼接,效率很低,而 StringBuffer 和 StringBuilder 的效率更高,同时 StringBuilder 的效率高于 StringBuffer。

3、StringBuffer 的方法是线程安全的,StringBuilder 是线程不安全的,在考虑线程安全的情况下,应该使用 StringBuffer。

6、下面代码的运行结果是?

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

Hello,因为 String 是不可变的,传入 test 方法的参数相当于 str 的一个副本,所以方法内只是修改了副本,str 本身的值没有发生变化。

7、下面代码的运行结果是?

public static void main(String[] args) {
  StringBuffer str = new StringBuffer("Hello");
  test(str);
  System.out.println(str);
}
public static void test(StringBuffer str){
  str.append(" World");
}

Hello World,因为 StringBuffer 是可变类型,传入 test 方法的参数就是 str 的引用,所以方法内修改的就是 str 本身。

参考文章:
How to Warm Up the JVM
每日一读

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值