聊一聊Java字符串的不可变
点击蓝色“程序猿DD”关注我
回复“资源”获取独家整理的学习资料!
前言
在 Java 开发中 String (字符串)对象是我们使用最频繁的对象,也是很重要的对象。正是使用得如此频繁,String 在实现层面上不断进行优化,从 Java6 到 Java7,再到 Java9 的新实现 ,都是为了提升 String 对象的性能,而其中不变的是 String 所生俱来的特性:不可变。本文主要聊一聊 String 的不可变,以及为什么存在的。
什么是 String 的不可变
首先我们先来看下什么是不可变对象:一旦对象被创建并初始化后,内部的状态数据就会保持不变。查看 JDK 源码中的 String 类,可以看到类本身被 final 修饰,并且内部的大部分属性都是 final 修饰的,除了字段 hash 是通过字符串内容计算并缓存起来的。这样的行为让 String 类无法被扩展,内部属性也无法被修改。
接着我们再来用画图的形式来说明下 String 的不可变性。
通常我们初始化字符串都是以下形式:
String 类型的引用变量 a
保留了一个字符串对象 string
的引用,就如同下图所示,箭头则表示了变量 a
与真正 String 对象的引用关系。
再通过上述代码,我们将变量 a
赋值给变量 b
,变量 b
也存储了字符串对象 string
的引用,它们指向的是同一个对象。
当我们尝试对变量 a
重新赋值,看下对变量 b
会不会有影响呢
想必小伙伴一看就知道,打印的结果肯定是 string2,string
(图片有误,应该是a=string2)同样用画图的方式展示这两个变量与字符串对象的引用关系。
将变量 a
重新赋值后,保存了新的引用,而不是直接在原有的字符串对象上进行数据改变,同时变量 b
仍然存的是对象 string
的引用,变量 a
和 b
两者相互独立,不影响,这也正是说明了 String 对象的不可变。
在这里初认 Java 的小伙伴还可能会有些困惑:对一个String对象 a
赋值 string
,然后又让 a
值为 string2
,这个时候a的值变成 了string2
, a
的值改变了,为什么还说 String 对象不可变呢。
其实问题也很简单,这里的 a
只是存储 String 对象的引用,并不是对象本身,a
存储的是指向对象所在内存的地址引用罢了,当第二次赋值时,a
引用指向了对象 string2
的内存地址,而对象 string2
是重新创建的,之前的 string
对象仍在内存中,并且由变量 b
引用着。
除此之外,String 类的返回 String 对象的方法不会改变自身,都是返回一个新的 String 对象来实现,比如 concat
,replace
,substring
等等。
为什么 String 需要不可变
聊完什么是 String 的不可变后,接下来我们再说说 String 为什么需要不可变呢,又有什么好处呢?
字符串常量池的实现
在Java中,我们通常有两种方式创建字符串对象,一种是通过字符串字面量方式创建,就如上文的代码,另外一种就是通过 new 方式去创建,如 String c = new String("string 3");
而两者区别就在于通过字符串字面量的方式创建时,JVM 会现在字符串池中检查字符串内容是否已经存在,如果存在就会直接返回对应的引用,而不是再次分配内存进行创建,如果不存在就会分配在内存中创建的同时将字符串数据缓存在字符串池中,便于重用。正是是由于字符串的不可变,同样的字符串内容可以让 JVM 可以减少额外的内存分配操作,直接使用在字符串池中字符串对象即可,对性能提升和内存节省都大有好处。
关于字符串池,这里稍微简单介绍一下: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之父的话说就是,“我会尽可能地使用不可变对象”。
留言交流不过瘾?添加微信:zyc_enjoy
根据指引加入各种主题讨论群
每日一问
今日问题:
王师傅是卖鱼的,一斤鱼进价45元,现亏本大甩卖,顾客35元买了一公斤,给了王师傅100元假钱,王师傅没零钱,于是找邻居换了100元。事后邻居存钱过程中发现钱是假的,被银行没收了,王师傅又赔了邻居100元,请问王师傅一共亏了多少?
(留言说说你的答案和解析吧,关注公众号,发送口令:Q20190824,核对正确答案)
昨日问答:点击>>查看<<
推荐阅读
这几款好用超赞的 Google Chrome插件送给你!
IntelliJ IDEA 2019.2最新解读
Spring Cloud与Dubbo的完美融合之手
狡猾的 AI 工程师,编个故事骗走 2 亿人民币...
日均7亿交易量,如何设计高可用的MySQL架构?
签到计划
活动介绍:自律到极致-人生才精致:第13期
活动奖励:《Spring Cloud微服务:入门、实战与进阶》 x 10
扫描下放二维码,签到参与
点一点“阅读原文”小惊喜在等你
聊一聊Java字符串的不可变相关推荐
- Java字符串真的不可变吗?
本文翻译自:Is a Java string really immutable? We all know that String is immutable in Java, but check the ...
- java 字符串赋值_灵魂拷问:为什么 Java 字符串是不可变的?
在逛 programcreek 的时候,发现了一些精妙绝伦的主题.比如说:为什么 Java 字符串是不可变的?像这类灵魂拷问的主题,非常值得深思.对于绝大多数的初级程序员来说,往往停留在"知 ...
- 灵魂拷问:为什么 Java 字符串是不可变的?
在逛 programcreek 的时候,发现了一些精妙绝伦的主题.比如说:为什么 Java 字符串是不可变的?像这类灵魂拷问的主题,非常值得深思. 对于绝大多数的初级程序员来说,往往停留在" ...
- 再见,Java字符串是不可变的
最近,又有好几个小伙伴问我这个问题:"二哥,为什么 Java 的 String 要设计成不可变的啊?"说实话,这也是一道非常经典的面试题,面试官超喜欢问.我之前写过这方面的文章,现 ...
- java 字符串面试_Java字符串面试问答
java 字符串面试 String is one of the most widely used Java Class. Here I am listing some important Java S ...
- Java 1.1.3 修改字符串、不可变字符串
修改字符串 String类没有提供用于修改字符串的方法.如果希望将 greeting 的内容修改为" Help!", 不能直接地将 greeting的最后两个位置的字符修改为 ' ...
- java字符串不可变_Java字符串真的是不可变的吗?
Java字符串真的是不可变的吗? 我们都知道StringJava 中是不可变的,但请检查以下代码: String s1 = "Hello World"; String s2 = & ...
- snmpset对象不可写_别再问了,好吗?Java字符串一定是不可变的
最近,又有好几个小伙伴问我这个问题:"二哥,为什么 Java 的 String 要设计成不可变的啊?"说实话,这也是一道非常经典的面试题,面试官超喜欢问.我之前写过这方面的文章,现 ...
- java 字符串连接_为什么 Java 要把字符串设计成不可变的
String是Java中一个不可变的类,所以它一旦被实例化就无法被修改.不可变类的实例一旦创建,其成员变量的值就不能被修改.不可变类有很多优势.本文总结了为什么字符串被设计成不可变的.将涉及到内存.同 ...
最新文章
- 苹果iCloud或在今年晚些时候支持游戏中心和苹果地图
- 【温故知新】CSS学习笔记(行高简介)
- 中间表增加额外字段_如何定制分表中间件
- HDU 1568 Fibonacci
- 如何让 Hyper-V 和 VMware 虚拟机软件共存?
- [渝粤教育] 扬州工业职业技术学院 微言品语文 参考 资料
- 前端学习(3324):你不知道javascript说闭包
- PyTorch 1.0 中文官方教程:对抗性示例生成
- java 配对问题_Java中的配对类是什么?
- python3之线程
- 长安链技术架构与共识模块介绍
- matlab 连续傅里叶变换,matlab快速傅里叶变换.ppt
- Phoenix升级:Error: Cluster is being concurrently upgraded from 4.7.x to 4.8.x.
- Ubuntu20.04设置静态IP
- 使用mybatis拦截器实现业务层和持久化层的数据处理、加密、解密、脱敏。
- 天津大学计算机软件学院,2019计算机考研天津大学数据科学与服务工程团队(与软件学院共建)...
- 机器学习从入门到创业手记-2.算法与导师
- Lambda 表达式详解
- 联想电脑怎么进入bios设置u盘启动
- 为什么电脑浏览器显示时钟快了_xp打开网页提示“你的时钟慢了”的原因及解决方法...
热门文章
- linux Rootkit:x86与ARM的内联内核函数Hooking
- TCP、UDP绑定同一端口通信的解释
- AfxOleInit()和::CoInitialize(NULL)区别
- 利用FreeNas创建AFP共享
- CentOS7.2基于LAMP搭建WordPress,并自定义Logo和名称
- 作文计算机使用有什么问题,关于电脑利弊的作文
- django oracle数据库配置,django连接oracle时setting 配置方法
- Design Pattern - Interpreter(C#)
- 陈老师Linux内核进程管理导学
- java 文本压缩_[Java基础]Java使用GZIP进行文本压缩