String,StringBuffer,StringBuilder有什么区别?

String,StringBuffer,StringBuilder有什么区别?

一、考点分析及深入探讨

  • 通过String和相关类,考察基本的线程安全设计和实现,各种基础编程实践

  • 考察JVM对象缓存机制的理解以及如何良好地使用

  • 考察JVM优化Java代码的一些技巧

  • String相关类的演进,比如Java9中实现的巨大变化

    。。。

二、概念

1、String

特点:

  • String是典型的immutable类,被声明为final class,所有属性都是final的

  • 由于他的不可变性,类似拼接,裁剪字符串等动作,都会产生新的String对象

  • String保证了基础线程安全,因为你无法对它内部数据进行任何修改,这种便利甚至体现在拷贝构造函数中,由于不可变,Immutable对象在拷贝时不需要额外复制数据

  • String操作不当可能会产生大量临时字符串

应用:

  • 字符串内容不经常发生变化的业务场景优先使用String类.例如:常量声明、少量的字
符串拼接操作等。如果有大量的字符串内容拼接, 避免使用String与String之间的 “+” 操
作。因为这样会产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)
2、StringBuffer

特点:

  • StringBuffer是为了解决上面提到拼接产生太多中间对象的问题提供的一个类
  • 可以用append或者add方法,把字符串添加到已有序列的末尾或者指定位置
  • StringBuffer本质是一个线程安全的可修改字符序列 ,通过把各种修改数据的方法都加上synchronized关键字实现。它保证了线程安全,也随之带来了额外的性能开销。
  • 所以除非有线程安全的需要,不然还是推荐使用StringBuilder。

应用:

  • 频繁进行字符串的运算(如拼接、替换、删除等),并且运行在多线程环境下。建议使
用StringBuffer ,例如XML解折、HTTP参数解析与封装。

备注:

  • 不必纠结于synchronized性能之类的,有人说“过早优化是万恶之源”,考虑可靠性,正确性和代码可读性才是大多数应用开发最重要的因素
3、StringBuilder

特点:

  • StringBuilder在能力上和StringBuffer没有本质的区别,但是它去掉了线程安全的部分,有效减少了开销,是绝大部分情况下进行字符串拼接的首选

应用:

  • 频繁进行字符串的运算(如拼接、替换、删除等),并且运行在单线程环境下,建议使
用StringBuilder, 例如SQL语句拼装、JSON封装等。

三、实现

1、如何修改字符序列
  • 为了实现修改字符序列的目的,StringBuffer和StringBuilder底层都是利用可修改的数组二者继承了AbstractStringBuilder。区别在于是否加了synchronized
  • 这个内部数组应该创建多大的呢?如果太小,拼接的时候可能要重新创建足够大的数组;如果太大,优惠浪费空间。目前的实现是,构建时初始字符串长度加16(即最小初始值为16)。
  • 我们如果确定拼接会发生非常多次,而且大概是可预计的,那么就可以指定合适的大小,避免很多次扩容的开销
  • 扩容会产生多重开销,因为要抛弃原有数组,创建新的数组,还要进行arrayCopy
2、字符串缓存

Ⅰ、 intern()显式排重机制

特点:

  • String在Java 6以后提供了 intern()方法,目的是提示JVM把相应字符串缓存来,以备重
复使用 。在创建字符串对象并调用intern()方法的时候,如果已经有缓存的字符串,就会
返回缓存里的实例,否则将其缓起来。

  • 一般来说, JVM会将所有的类似“abc"这样的文本字
符串,或者字符串常量缓存起来 ,但是实际情况让你大跌眼镜。Java6这种历史版本不推荐大量使用intern(),

    原因是**被缓存的字符串存在所谓的PermGen(永久代)**里
的,这个空间是很有限的,也基本不会被FullGC之外的垃圾收集照顾到。所以如果使用不当,就会出现OOM。

  • 在后序版本中,这个缓存被放置在堆中,这样就极大避免了永久代占满的问题,甚至永久代在 JDK8 被MetaSpace(元数据区)替代了。

  • Intern是一种显式排重机制,但是它也有一定的副作用,因为需要开发者写代码时明确调
用,一是不方便,每一个显式调用是非常麻烦的;另外就是我们很难保证效率。应用开发阶段
很难淸楚地预计字符串的重复情况,有人认为这是染代码的实践。

改进:

  • JDK8之后,推出了一个新的特性,也就是G1 GC下的字符串排重.它是通过将相同数据的字符串指向同一份数据来做到的,是JVM底层的改变,并不需要Java类库

    做什么修改

  • 注意G1 GC这个功能目前是默认关闭的,你需要使用下面参数开启,并且记指定使用G1 GC :
-XX:+UseStringDeduplication

Ⅱ、Intrinsic:

  • 前面说到的几个方面.只是Java底层对字符串各种优化的一角,在运行时,字符串的
操作会直剧用JVM内部的Intrinsic机制,往往运行的就是特殊优化的本地代码,而根本就不
是Java代码生成的字节码。Intrinsic可以简单理解为,是一种利用native方式hard-coded
的逻辑,算是一种特别的内联。很多优化还是需要直接使用特定的CPU指令,具体可以看相关
源码
3、String自身的演化
  • 如果你仔细观察过Java的字符串,在历史版本中,它是使用char数组来数据的,这样非常
直接。但是Java中的char是两个bytes大小,拉丁语系语言的字符,根本就不需要太宽的
char,这样无区别的实现就造成了一定的浪费密度是编程语言平台永恒的话题,因为归根结底
绝大部分任务是要来操作数据的.

  • 在Java 6就提供了压缩字符串的特性,但是这个特性的实现并不是开
源的,而且在实践中也暴露出了一些问题,所以在最新的JDK版本中已经将它移除了。

  • 在Java 9中,我们引入了 Compact Strings的设计,对字符串进行了大刀阔斧的改进,将数据
存储方式从char数组,改变为一个byte数组加上一个标识编码的所谓coder,并且将相关字符串操作类进行了修改。

  • 在通用的性能测试和产品实验中,我们能非常明显地看到紧凑字符串带来的优势,即更小的内存
占用、更快的操作速度

4、备注:
  • “可以看出,仅仅是字符串一个实现,就需要Java平台工程师和科学家付出如此大且默默无闻的
努力,我们得到的很多便利都是来源于此”
  • 我想说的是,对技术要永葆好奇心,并保持谦虚学习的心态,这是对科学家们最好的尊重和敬畏。

四、网友问答

1、网友1

  • String

在这里插入图片描述

  • StringBuffer/StringBuilder
    在这里插入图片描述

五、参考文档

  • 极客时间第五讲:String、StringBuffer、StringBuilder有什么区别?
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值