转载来源: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. 拼写检查-c++

    [问题描述] 作为一个新的拼写检查程序开发团队的成员,您将编写一个模块,用已知的所有形式正确的词典来检查给定单词的正确性.        如果字典中没有这个词,那么可以用下列操作中的一个来替换正确的单 ...

  2. c++对c的拓展_命名空间_简单使用

    名字的控制:c可使用static关键字使该关键字在本单元内可见,c++则使用命名空间对名字的可见性及产生进行控制 命名空间:控制标识符的作用域(本质上就是一个作用域) 使用特点:1.必须定义在全局范围 ...

  3. Java多线程与线程池技术

    一.序言 Java多线程编程线程池被广泛使用,甚至成为了标配. 线程池本质是池化技术的应用,和连接池类似,创建连接与关闭连接属于耗时操作,创建线程与销毁线程也属于重操作,为了提高效率,先提前创建好一批 ...

  4. sourceCRT设置全局字符集为utf-8

    以前刚打开服务器crt字符集都会默认是default模式,搞得每次都要手动设置成UTF-8. 烦躁. 将CRT全局字符集设置成UTF-8格式方法: 设置窗口不会断掉: 即每100s发送一次ls \n ...

  5. centos7源码安装mysql5.7.19

    centos7源码包安装mysql5.7 5.7.20安装方法和5.7.19的一样. 1.安装前准备 清空环境.安装相应的软件包 1>关闭防火墙和SELinux 2>配置yum源(阿里云, ...

  6. python的for循环基本用法

    for循环 for循环能做到的事情 while循环都可以做到 但是for循环语法更加简洁 并且在循环取值问题上更加方便 name_list = ['jason', 'tony', 'kevin', ' ...

  7. 『现学现忘』Git基础 — 4、Git下载与安装

    目录 1.Git下载 2.Git在Windows下的详细安装 3.验证Git是否安装成功 1.Git下载 进入官方地址下载Git客户端:https://git-scm.com/download/win ...

  8. Vue 中 axios 跨域配置 (!!!配置完成需要重新运行,不然也不起作用)

    当拿到一个网址如:https://music.163.com/store/api/categorypage/list  获取数据是出现如下: 证明该网址不能非常直观的拿到数据.接下来我们试试跨域拿这个 ...

  9. switch语法

    1. js 代码 // 1. switch 语句也是多分支语句 也可以实现多选1 // 2. 语法结构 switch 转换.开关 case 小例子或者选项的意思 // switch (表达式) { / ...

  10. 数据建模软件Chiner,颜值与实用性并存

    目录 一.chiner介绍 二.值得关注的功能点 2.1. 兼容各种格式的数据建模文件 2.2. 支持多数据库.代码生成 2.3. 支持逻辑视图与物理视图设计 2.4. 自动生成数据库文档 三.总结 ...