聊一聊 Java 字符串的不可变

粉妆玉砌
• 阅读 993

前言

在 Java 开发中 String (字符串)对象是我们使用最频繁的对象,也是很重要的对象。正是使用得如此频繁,String 在实现层面上不断进行优化,从 Java6 到 Java7,再到 Java9 的新实现 ,都是为了提升 String 对象的性能,而其中不变的是 String 所生俱来的特性:不可变。本文主要聊一聊 String 的不可变,以及为什么存在的。

什么是 String 的不可变

首先我们先来看下什么是不可变对象:一旦对象被创建并初始化后,内部的状态数据就会保持不变。查看 JDK 源码中的 String 类,可以看到类本身被 final 修饰,并且内部的大部分属性都是 final 修饰的,除了字段 hash 是通过字符串内容计算并缓存起来的。这样的行为让 String 类无法被扩展,内部属性也无法被修改。

接着我们再来用画图的形式来说明下 String 的不可变性。

通常我们初始化字符串都是以下形式:聊一聊 Java 字符串的不可变

String 类型的引用变量 a 保留了一个字符串对象 string 的引用,就如同下图所示,箭头则表示了变量 a 与真正 String 对象的引用关系。

聊一聊 Java 字符串的不可变

聊一聊 Java 字符串的不可变

再通过上述代码,我们将变量 a 赋值给变量 b ,变量 b 也存储了字符串对象 string的引用,它们指向的是同一个对象。

聊一聊 Java 字符串的不可变

当我们尝试对变量 a 重新赋值,看下对变量 b 会不会有影响呢

聊一聊 Java 字符串的不可变

想必小伙伴一看就知道,打印的结果肯定是 string2,string,同样用画图的方式展示这两个变量与字符串对象的引用关系。

聊一聊 Java 字符串的不可变

将变量 a 重新赋值后,保存了新的引用,而不是直接在原有的字符串对象上进行数据改变,同时变量 b 仍然存的是对象 string 的引用,变量 ab 两者相互独立,不影响,这也正是说明了 String 对象的不可变。

在这里初认 Java 的小伙伴还可能会有些困惑:对一个String对象 a 赋值 string,然后又让 a 值为 string2,这个时候a的值变成 了string2, a 的值改变了,为什么还说 String 对象不可变呢。

其实问题也很简单,这里的 a 只是存储 String 对象的引用,并不是对象本身,a 存储的是指向对象所在内存的地址引用罢了,当第二次赋值时,a 引用指向了对象 string2的内存地址,而对象 string2 是重新创建的,之前的 string 对象仍在内存中,并且由变量 b 引用着。

除此之外,String 类的返回 String 对象的方法不会改变自身,都是返回一个新的 String 对象来实现,比如 concatreplacesubstring 等等。

聊一聊 Java 字符串的不可变

为什么 String 需要不可变

聊完什么是 String 的不可变后,接下来我们再说说 String 为什么需要不可变呢,又有什么好处呢?

字符串常量池的实现

在Java中,我们通常有两种方式创建字符串对象,一种是通过字符串字面量方式创建,就如上文的代码,另外一种就是通过 new 方式去创建,如 String c = new String("string 3"); 而两者区别就在于通过字符串字面量的方式创建时,JVM 会现在字符串池中检查字符串内容是否已经存在,如果存在就会直接返回对应的引用,而不是再次分配内存进行创建,如果不存在就会分配在内存中创建的同时将字符串数据缓存在字符串池中,便于重用。正是是由于字符串的不可变,同样的字符串内容可以让 JVM 可以减少额外的内存分配操作,直接使用在字符串池中字符串对象即可,对性能提升和内存节省都大有好处。

聊一聊 Java 字符串的不可变

关于字符串池,这里稍微简单介绍一下:Java 的字符串池属于 JVM 专门给指定的特殊内存区域,用来存储字符串字面量。在 Java 7 之前,分配于 JVM 的方法区内,属于常量池的一部分;而 Java7 之后字符串池被移至堆内存进行管理,这样的好处就是允许被 JVM 进行垃圾回收操作,将未被引用的字符串所占内存即使回收,以此节省内存。

Hashcode 缓存

字符串作为基础的数据结构,大量地应用在一些集合容器之中,尤其是一些散列集合,在散列集合中,存放元素都要根据对象的 hashCode() 方法来确定元素的位置。由于字符串 hashcode 属性不会变更,保证了唯一性,使得类似 HashMap,HashSet 等容器才能实现相应的缓存功能。由于 String 的不可变,避免重复计算 hashcode,只有使用缓存的 hashcode 即可,这样一来大大提高了在散列集合中使用 String 对象的性能。

线程安全

在多线程中,只有不变的对象和值是线程安全的,可以在多个线程中共享数据。由于 String 天然的不可变,当一个线程”修改“了字符串的值,只会产生一个新的字符串对象,不会对其他线程的访问产生副作用,访问的都是同样的字符串数据,不需要任何同步操作。

安全性

由于字符串无论在任何 Java 系统中都广泛使用,会用来存储敏感信息,如账号,密码,网络路径,文件处理等场景里,保证字符串 String 类的安全性就尤为重要了,如果字符串是可变的,容易被篡改,那我们就无法保证使用字符串进行操作时,它是安全的,很有可能出现 SQL 注入,访问危险文件等操作。

结语

通过本文,我们介绍 String 是不可变的,可以将它们的引用可以被当作一个普通的变量来使用,无论是在方法间,还是线程间传递它们,都不用担心它指向的实际 String 对象发生改变,并且不可变的特性也在语言层面和程序层面上带了许多好处,在平常编程实践中我们也应该多学习效仿,用 James Gosling,Java之父的话说就是”我会尽可能地使用不可变对象“。

聊一聊 Java 字符串的不可变

推荐阅读

参考资料

点赞
收藏
评论区
推荐文章
阿里最新面试必备项之Java的String类,持续更新中!
最新腾讯面试必备项之Java的String类,持续更新中!1.1String的特性String类:代表字符串。Java程序中的所有字符串字面值(如“abc”)都作为此类的实例实现。String是一个final类,代表不可变的字符序列。String字符串是常量,用双引号引起来表示。他们的值在创建之后不能更改。String对象的找字符内容是存储在一个
Wesley13 Wesley13
3年前
java中stringBuilder的用法
String对象是不可改变的。每次使用System.String类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的String对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用System.Text.StringBuilder类。例如,当在一个循
面试字节我被String类的问题给问死了!
""是怎么连接字符串的?(JDK1.7及以上)总结:使用""连接字符串时,实际上是使用临时创建的StringBuilder对象来辅助完成的。对于编译时常量,在编译后直接计算出字符串的值,而不会在运行时创建临时的StringBuilder对象来完成字符串连接。在循环中对String对象进行连接,应该直接使用StringBuilder代替"",这样
Wesley13 Wesley13
3年前
String、StringBuffer、StringBuilder的区别
String字符串常量StringBuffer字符串变量(线程安全)StringBuilder字符串变量(非线程安全) 简要的说,String类型和StringBuffer类型的主要性能区别其实在于String是不可变的对象,因此在每次对String类型进行改变的时候其实都等同于生成了一个新的String
Wesley13 Wesley13
3年前
java中如何处理高并发
1 从最基础的地方做起,优化我们写的代码,减少必要的资源浪费。 a、避免频繁的使用new对象,对于整个应用只需要存在一个实例的类,我们可以使用单例模式。对于String连接操作,使用StringBuffer或StringBuilder,对于工具类可以通过静态方法来访问。 b、避免使用错误的方式,尽量不用instanceof做条件判断。使用java
子非鱼 子非鱼
3年前
引用不可变和对象不可变
什么是不可变的对象呢?我们都知道String是不可变的,如果有涉及大量的字符串拼接我们最好不要用String,虽然我们在代码中可以这样写:javaStringstr"test";str"test1";这样写是没有错的,这是不是和我们所说的String类型时不可变的违背了呢?其实不然,我们这里看到str“改变”了值,其实不是不是真正改变了,而是改变
东方客主 东方客主
4年前
一文弄懂Java中String的所有小秘密
简介String是java中非常常用的一个对象类型。可以说java中使用最多的就是String了。那么String到底有哪些秘密呢?接下来本文将会一一讲解。String是不可变的String是不可变的,官方的说法叫做immutable或者constant。String的底层其实是一个Char的数组。priv
Wesley13 Wesley13
3年前
Java中有趣的String、StringBuffer与StringBuilder
String介绍  String类属于java.lang包中,String类是不可变类,任何对String的改变都会引发新的String对象的生成。  创建String的两种方式:  1.通过构造器创建:StringstrnewString("Iamastring.");2.通过变量赋值:String
Wesley13 Wesley13
3年前
Java深究之String、StringBuffer、StringBuilder的区别
       在Java学习中,String、StringBuffer、StringBuilder三者是很重要的,在编写代码中经常使用到他们,那么深入的了解他们的异同是非常重要的,接下里我们详细剖析下这三个的异同之处_首先总结下这三者的区别:_1.基本区别String的对象不可变,StringBuffer和StringBuild
Wesley13 Wesley13
3年前
JAVA常用类_String
String:一、定义1.字符串,声明为final的,不可被继。2.实现了Serializable接口:表示字符串是支持序列化的。  实现了Comparable接口:表示String可以比较大小二、特性1.String的不可变性    1.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
Wesley13 Wesley13
3年前
Java学习:字符串概述与特点
字符串概述与特点java.lang.String类代表字符串API当中说:Java程序中的所有字符串字面值(如“abc“)都作为此类的实例实现。其实就是说:程序当中所用的双引号字符串,都是String类的对象。(就算没有new,也照样是。)字符串的特点1.字符串的内容永远不可改变。【重点】
粉妆玉砌
粉妆玉砌
Lv1
洛阳城里见秋风,欲作家书意万重。
文章
8
粉丝
0
获赞
0