转载来源:String为什么不可变

今天来分享一道群友去阿里云面试遇到的 Java 基础面试真题:“StringStringBufferStringBuilder 的区别?String 为什么是不可变的?”。

网站很多文章都把 String 不可变的原因讲错了,建议你重点关注一下。另外,本文还提到了 :“Java 9 为何要将 String 的底层实现由 char[] 改成了 byte[] ?”

下面是正文。

可变性

简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,所以String 对象是不可变的。×

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
//...
}

修正 :我们知道被 final 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,final 关键字修饰的数组保存字符串并不是 String 不可变的根本原因,因为这个数组保存的字符串是可变的(final 修饰引用类型变量的情况)。

String 真正不可变有下面几点原因:

  1. 保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
  2. String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。

相关阅读:如何理解 String 类型值的不可变?- 知乎提问[1]

补充(来自issue 675[2]):在 Java 9 之后,StringStringBuilderStringBuffer 的实现改用 byte 数组存储字符串。

Java 9 为何要将 String 的底层实现由 char[] 改成了 byte[] ?

新版的 String 其实支持两个编码方案:Latin-1 和 UTF-16。如果字符串中包含的汉字没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案。Latin-1 编码方案下,byte 占一个字节(8 位),char 占用 2 个字节(16),byte 相较 char 节省一半的内存空间。

如果字符串中包含的汉字超过 Latin-1 可表示范围内的字符,bytechar 所占用的空间是一样的。

这是官方的介绍:https://openjdk.java.net/jeps/254

StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串,不过没有使用 finalprivate 关键字修饰,最关键的是这个 AbstractStringBuilder 类还提供了很多修改字符串的方法比如 append 方法。

abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
//...
}

线程安全性

String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilderStringBuilderStringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacityappendinsertindexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

性能

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结:

  1. 操作少量的数据: 适用 String
  2. 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

String 为什么不可变?的更多相关文章

  1. 从字节码和JVM的角度解析Java核心类String的不可变特性

    1. 前言 最近看到几个有趣的关于Java核心类String的问题. String类是如何实现其不可变的特性的,设计成不可变的好处在哪里. 为什么不推荐使用+号的方式去形成新的字符串,推荐使用Stri ...

  2. JAVA 没有重载运算符,那么 String 类型的加法是怎么实现的,以及String类型不可变的原因和好处

    1, JAVA 不具备 C++ 和 C# 一样的重载运算符 来实现类与类之间相互计算 的功能    这其实一定程度上让编程失去了代码的灵活性, 但是个人认为,这在一定程度上减少了代码异常的概率     ...

  3. 在Java中String类为什么要设计成final?String真的不可变吗?其他基本类型的包装类也是不可变的吗?

    最近突然被问到String为什么被设计为不可变,当时有点懵,这个问题一直像bug一样存在,竟然没有发现,没有思考到,在此总结一下. 1.String的不可变String类被final修饰,是不可继承和 ...

  4. 对String值不可变的理解以及String类型的引用传递问题

    今天复习java时,突然注意到了一句以前没有注意过的一句话,String 是final修饰的,其值是不可变的.当时看的一脸懵逼,String str = "abc"; str = ...

  5. String为什么不可变

    转载:http://www.importnew.com/7440.html https://www.cnblogs.com/leskang/p/6110631.html 什么是不可变对象? 众所周知, ...

  6. String 与不可变对象

    什么是不可变对象 ?不可变对象指的是在创建一个对象之后 ,不能再改变它的状态 ,那么这个对象就是不可变的 .不能改变状态的意思是 ,不能改变对象内的成员变量 ,包括基本数据类型的值不能改变 ,引用类型 ...

  7. 为什么字符串String是不可变字符串&&"".equals(str)与str.equals("")的区别

    为什么字符串String是不可变字符串 实际上String类的实现是char类型的数组 虽然说源码中设置的是private final char[] value; final关键词表示不可变动 但是只 ...

  8. String 的不可变真的是因为 final 吗?

    尽人事,听天命.博主东南大学硕士在读,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 「CS-Wiki」Gitee ...

  9. C# string 是不可变的,指什么不可变

    String 表示文本,即一系列 Unicode 字符.字符串是 Unicode 字符的有序集合,用于表示文本.String 对象是 System.Char 对象的有序集合,用于表示字符串.Strin ...

随机推荐

  1. mysql获取表的列名

    DESC test4 SHOW COLUMNS FROM test4 SELECT COLUMN_NAME  FROM information_schema.columns WHERE table_n ...

  2. add jars、add external jars、add library、add class folder的区别

    add external jars = 增加工程外部的包add jars = 增加工程内包add library = 增加一个库add class folder = 增加一个类文件夹add jar是表 ...

  3. ELK日志保留7天-索引生命周期策略

    一.简介 ELK日志我们一般都是按天存储,例如索引名为"kafkalog-2022-04-05",因为日志量所占的存储是非常大的,我们不能一直保存,而是要定期清理旧的,这里就以保留 ...

  4. asp.net core启动源码以及监听,到处理请求响应的过程

    摘要 asp.net core发布至今已经将近6年了,很多人对于这一块还是有些陌生,或者说没接触过:接触过的,对于asp.net core整个启动过程,监听过程,以及请求过程,响应过程也是一知半解,可 ...

  5. GO语言学习——基本数据类型——整型、浮点型、复数、布尔值、fmt占位符

    基本数据类型 整型 整型分为以下两个大类: 按长度分为:int8.int16.int32.int64 对应的无符号整型:uint8.uint16.uint32.uint64 其中,uint8就是我们熟 ...

  6. go interface{}使用

    先上代码 func In(haystack []interface{}, needle interface{}) (bool, error) { sVal := reflect.ValueOf(hay ...

  7. HCIE笔记-第二节-数据封装+传输介质

    数据传输的形式 1.电路交换 在通信之前,维护一条逻辑意义上的链路,这条链路仅仅可以传递两者的数据 2.报文交换 在数据之外,加上能够标识接收者.发送者的信息 3.分组交换(最主流) 依然进行报文交换 ...

  8. css兼容问题集锦

    BEGIN; 1.文本框很大,导致里面的内容不居中.以及内容为数字时,不支持text-indent属性 解:line-height: K px; (值为文本框的height值). 2.文本框有背景图片 ...

  9. 前台主页搭建、后台主页轮播图接口设计、跨域问题详解、前后端互通、后端自定义配置、git软件的初步介绍

    今日内容概要 前台主页 后台主页轮播图接口 跨域问题详解 前后端打通 后端自定义配置 git介绍和安装 内容详细 1.前台主页 Homeviwe.vue <template> <di ...

  10. [RPC学习]Dubbo+nacos实现动态更新内存RTree

    1.背景 服务架构一般都是从 单体架构 -> 微服务架构 -> 分布式架构 的迭代,我上一家公司就是在业务发展到一定规模时,开始拆老的单体服务,按业务维度拆成多个微服务,服务之间用的是HT ...