String、StringBuffer、StringBuilder有什么区别?

关于String、StringBuffer、StringBuilder在刚开始面试的时候经常被问到的问题。这个问题主要考察对字符串的操作,主要有:

  • 字符串的拼接设计(是否产生新的对象)
  • 缓存
  • 执行速度
  • 线程安全
  • 在jdk8之后进行的优化
    另外延伸问题就有String源码的设计比如:hashcode的重新、对象如何进行比较的算法等。

首先比较常见的回答:

  • String被声明为final class,是典型的Immutable 不可变类。所以所有的属性也都是final的,在进行拼接、裁剪字符串的时都会产生新的String对象。

  • StringBuffer是为了解决String拼接产生太多中间对象问题而提供的一个类,StringBuffer是可变的,我们经常用到的append()、insert()等方法进行字符串拼接。StringBuffer是一个线程安全的可修改字符序列,它的线程安全是通过在各个修改数据的方法上加了synchronized关键字来实现的。为了实现线程安全,也带来了额外的性能开销。所以除非有线程安全的必要,尽量使用StringBuilder。

  • StringBuilder在处理字符串功能上和StringBuffer基本一致。也是可变的。只是在修改数据的方法上删除了synchronized关键字。不是线程安全的。但是可以有效减小性能开销。大部分情况会使用StringBuilder进行字符串操作。
  • 一般情况下,String进行字符串拼接会产生中间对象并且进行回收(如果是使用+进行拼接,并且缓存中存在则不会产生新对象),所以String执行速度比StringBuffer慢;又因为StringBuffer是线程安全的,所以StringBuffer比StringBuilder慢。

那么我们做字符串拼接的时候是不是就得使用StringBuilder来提高速度呢?其实有些情况不是这样的。比如:

public static void main(String[] args) {
    String str = "aa" + "bb" + "cc" + "dd" + "xingguo";
}

对于这样的一串字符串,如果使用StringBuilder来写,就会多敲很多code.那实现的效果怎么样呢?
通过编辑和反编译可以看一下:

public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String aabbccddxingguo
       2: astore_1
       3: return
}

再打开编译后的class文件看下:
这里写图片描述
所以如果是这种字符串的拼接可以直接使用String进行拼接。

那么在String比较的时候,有时两个看似相同的字符却不相等。又是什么原因呢?

public static void main(String[] args) {
        String str1 = "aa";
        String str2 = "aa";
        String str3 = new String("aa");
        String str4 = new String("aa");

        System.out.println("str1 == str2:"+(str1 == str2));
        System.out.println("str1 == str3:"+(str1 == str3));
        System.out.println("str1 == str4:"+(str1 == str4));

        System.out.println("str3 == str4:"+(str3 == str4));
    }
结果:
str1 == str2:true
str1 == str3:false
str1 == str4:false
str3 == str4:false

首先我们知道==比较的是对象的引用。(这里会延伸到equals/hashcode的问题)。
从运行结果可以看出str1、str3、str4是不相同的,这个比较容易理解。因为String类是不可变的,每次都会生成一个新的对象,所以不相同。
那么为什么str1和str2会相同呢?这里就涉及到String中的缓存机制了。
像String str1 = “aa”;创建的过程是:

  • 首先在缓存池中查询有没有“aa”字符串对象
  • 如果不存在,则创建“aa”,并让str1引用该对象
  • 如果存在则直接引用该对象

所以执行str1这一行的时候,缓存池中没有“aa”所以先创建一个对象。执行str2这一行的时候,直接指向了同一个对象。
而像String str3 = new String(“aa”);创建的过程是:

  • 先在堆上创建一个对象“aa”,并让str3引用该对象
  • 然后在缓存池中查看,是否存在内容为“aa”的字符串对象
  • 若存在,则将new出来的字符串对象和缓存池中的对象关联起来
  • 若不存在,则在缓存池创建一个内容为“aa”的对象,并将堆中的对象与之关联。使用intern()方法可以返回字符串在缓存池中的对象引用。

我们再来测试一下这个创建过程:

public static void main(String[] args) {
    String str3 = new String("aa");
    String str5 = new String("aa").intern();
    String str1 = "aa";
    System.out.println("str1 == str3:"+(str1 == str3));
    System.out.println("str1 == str5:"+(str1 == str5));
}
结果:
str1 == str3:false
str1 == str5:true

在判断两个字符串是否相同是时候,一般会使用到==、equals、hashCode来判断。

  • ==是对象内存的判断,如果对象内存一致则为true,否则在为false
  • equals则是对字符串内容的比较,如果字符串内容相同则为true。还有一个equalsIgnoreCase()方法忽略大小写
  • hashCode则是对字符串数值化的比较。由于String的hashCode计算方法的原因,即使两个hashCode相同,也不能保证==为true,equals为true.
public static void main(String[] args) {
    String str1 = "aa";
    String str2 = new String("aa");
    String str3 = "Aa";

    System.out.println(str1.equals(str2));//true
    System.out.println(str1.equals(str3));//false
    System.out.println(str1.equalsIgnoreCase(str3));//true

}

可以通过hashCode的方法可知:即使两个hashCode相同,也不能保证==为true,equals为true

public int hashCode() {
       int h = hash;
       if (h == 0 && value.length > 0) {
           char val[] = value;

           for (int i = 0; i < value.length; i++) {
               h = 31 * h + val[i];
           }
           hash = h;
       }
       return h;
   }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值