最新要闻
- 天天观焦点:兰州高新区重点项目建设实现一季度开门红
- 全球视讯!华为MateBook D14/D16今日开售:13代酷睿 4999元起
- 有人姓“苟”:担心孩子受嘲笑而改姓_环球新视野
- 天天日报丨五一假期用得着!冈本okamoto超薄安全套大促:立省60元
- 全球观热点:国产鼠标卷疯了 200元碾压千元国际大牌
- 黄海附近发生4.8级地震:上海等地有震感 焦点播报
- 天天热门:洞口县:刘玮到江口镇调研督导工作,与结对帮带年轻干部谈心谈话
- 重庆警方破获特大制售假冒汽车安全气囊案 涉案金额逾2亿元|天天热文
- 全球速讯:天涯社区已无法打开:网友掀悼念潮
- 《原神》开发商最新力作!米哈游《崩坏:星穹铁道》今日公测 每日报道
- 天天精选!卫星通信首次下放!华为nova 11系列首销:2499元起
- 网传眉山夜市有人偷小孩?警方:男子并非人贩,疑用假币引纠纷 正进一步调查
- 长三角楼市:3月量价齐升
- 1台利润是安卓机10倍!iPhone翻新机全球卖爆:系统不卡 保值好|环球快播报
- 天天观热点:比OLED更香!苹果即将商用MicroLED技术:不烧屏
- 十四届全国人大常委会举行第三次委员长会议,赵乐际主持-环球观天下
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
Java基础之String字符串的底层原理,面试常见问题
前言
在之前的两篇文章中,给大家介绍了String字符串及其常用的API方法、常用编码、正则表达式等内容,但这些内容都是停留在”如何用“的阶段,没有涉及到”为什么“的层面。实际上,我们在求职时,面试官很喜欢问我们关于String的一些原理性知识,比如String的不可变性、字符串的内存分配等。为了让大家更好地应对面试,并理解String的底层设计,接下来会给大家聊聊String的一些原理,比如String为什么具有不可变性?
全文大约 【4000】字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......
(资料图片)
一. Spring源码中的final关键词
为了弄清楚String为什么具有不可变性,我们先来看看String的源码,尤其是源码中带有final关键词的地方。
1. final的特点
为了更好地理解String相关的内容,在阅读String源码之前,我们先来复习一下final关键词有哪些特点,因为在String中会涉及到很多final相关的内容。
1.final关键词修饰的类不可以被其他类继承,但是该类本身可以继承其他类,通俗的说就是这个类可以有父类,但是不能有子类;
2.final关键词修饰的方法不可以被覆盖重写,但是可以被继承使用;
3.final关键词修饰的基本数据类型变量称为常量,只能被赋值一次;
4.final关键词修饰的引用数据类型的变量值为地址值,地址值不能改变,但是地址内的数据对象可以被改变;
5.final关键词修饰的成员变量,需要在创建对象前赋值,否则会报错(即需要在定义时直接赋值,如果是在构造方法中赋值,则多个构造方法均需赋值)。
复习了final的特点之后,接下来我们就可以阅读String的源码了。
2. String源码解读
接下来就请大家请跟着我们来看看String源码中关于不可变性的内容吧。
2.1 final修饰的String类
/*** ......其他略......** Strings are constant; their values cannot be changed after they* are created. String buffers support mutable strings.* Because String objects are immutable they can be shared. For example:** ......其他略......**/public final class Stringimplements java.io.Serializable, Comparable, CharSequence {......
我们先对上面的源码及其注释进行简单的解释:
●final:请参考第1小节对final特点的介绍;
●Serializable:用于序列化;
●Comparable
:默认的比较器; ●CharSequence: 提供对字符序列进行统一、只读的操作。
从这一段源码及注释中,我们可以得出如下结论:
●String类用final关键字修饰,说明String不可被继承;
●String字符串是常量,字符串的值一旦被创建,就不能被改变;
●String字符串缓冲区支持可变字符串;
●因为String对象是不可变的,所以它们是可以被共享的。
2.2 final修饰的value[]属性
public final class Stringimplements java.io.Serializable, Comparable, CharSequence {/** The value is used for character storage. */private final char value[];......
从源码中可以看出,value[]是一个私有的字符数组,String类其实就是通过这个char数组来保存字符串内容的。简单的说,我们定义的字符串会被拆成一个一个的字符,这些字符都被存放在这个value字符数组里面。
这里的value[]数组被final修饰,初始化之后就不能再被更改。但是大家注意,我们这里说的value[]不可变,指的是value的引用地址不可变,但是value数组里面的数据元素其实是可变的!这是因为value是数组类型,根据我们之前学过的知识,value的引用地址会分配在栈中 ,而其对应的数据是在常量池中保存的。所以我们说String不可变,指的就是value在栈中的引用地址不可变,而不是说常量池中数组本身的数据元素不可变。
另外我们要注意,Java中的字符串常量池,用来存储字符串字面量! 但是由于JDK版本的不同,常量池的位置也不同:
JDK 6 及以下版本的字符串常量池是在方法区(Perm Gen)中,此时常量池中存储的是字符串对象;在 JDK 8.0 中,方法区(永久代被元空间取代了;
JDK 7、8以后的字符串常量池被转移到了堆中,此时常量池存储的就是字符串对象的引用,而不是字符串对象本身。
至此,我们就带各位把String类中的核心源码分析完了,接下来我们再进一步分析String不可变的原因,及其他底层原理设计。
二. String的不可变性
1. 实验案例
了解了上面的这些核心源码之后,接下来再带各位来验证一下,看看String到底能不能变!我先给各位来一段案例代码,代码案例如下图所示。
结果s的内容变了,好像是啪啪打脸了???!!!咋回事,你不是说了String不可变吗?怎么这么快就翻车打脸了?别急,让我们好好来分析一下。
2. 结果剖析
首先我们从结果上来看String s 变量的结果好像改变了,但为什么我们又说String是不可变的呢?
要想明白这个问题,我们得先弄清楚一个点,即引用和值的区别!在上面的代码中,我们先是创建了一个 "yiyige" 为内容的字符串引用s,s其实先是指向了value对象,而value对象则指向存储了 "y,i,y,i,g,e" 字符的字符数组。
因为value被final修饰,所以value的值不可被更改。因此,上面代码中改变的其实是s的引用指向,而不是改变了String对象的值。换句话说,上面实例中 s的值 只是 value的引用地址,并不是String内容本身!当我们执行 s = "yyg"; 语句时,Java中会创建一个新的字面量对象 "yyg",而原来的 "yiyige" 字面量对象依然存在于内存的intern缓存池中。在Java中,因为数组也是对象, 所以value中存储的也只是一个引用,它指向一个真正的数组对象。在执行了String s = “yiyige”; 这句代码之后,真正的内存布局应该是下图这样的:
因为value是String封装的字符数组,value中的所有字符都属于String这个对象。由于value是private的,且没有提供setValue等公共方法来修改这个value值,所以在String类的外部是无法修改value值的,也就是说一旦初始化就不能被修改。此外,value变量是final的, 也就是说在String类内部,一旦这个值初始化了,value这个变量所引用的地址就不会改变了,即一直引用同一个对象。正是基于这一层,所以说String对象是不可变的对象。
但其实value所引用对象的内容完全可以发生改变,我们可以利用反射来消除String类对象的不可变特性。
所以String的不可变性,指的是value在栈中的引用地址不可变,而不是说常量池中array本身的数据元素不可变!
而String对象的改变实际上是通过内存地址的 “断开-连接”变化来完成的,这个过程中原字符串中的内容并没有任何的改变。String s = "yiyige"; 和 s = "yyg"; 实质上是开辟了2个内存空间,s 只是由原来指向 "yiyige" 变为指向 "yyg" 而已,而其原来的字符串内容,是没有改变的,如下图所示。
因此,我们在以后的开发中,如果要经常修改字符串的内容,请尽量少用String,因为字符串的指向“断开-连接”会大大降低性能,建议使用:StringBuilder、StringBuffer。
那么String一定不可变吗?有没有办法让String真的可变呢?我们继续往下学习!
三. String真的不可变吗?
1. 实验案例
我在前面的章节中给大家说,String的不可变,其实指的是String类中value属性在栈中的引用地址不可变,而不是说常量池中array本身的数据元素不可变!也就是说String字符串的内容其实是可变的!那怎么实现呢?利用反射就可以实现,我们通过一个案例来证明一下。
try {String str = "yyg";System.out.println("str=" + str + ", 唯一性hash值=" + System.identityHashCode(str));Class stringClass = str.getClass();//获取String类中的value属性Field field = stringClass.getDeclaredField("value");//设置私有成员的可访问性,进行暴力反射field.setAccessible(true);//获取value数组中的内容char[] value = (char[]) field.get(str);System.out.println("value=" + Arrays.toString(value));value[1] = "z";System.out.println("str=" + str + ", 唯一性hash值=" + System.identityHashCode(str));} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}
2. 结果剖析
上面案例的执行结果如下图所示:
我们可以看到,String字符串的字符数组可以通过反射进行修改,导致字符串的“内容”真的发生了变化!并且我们又利用底层的java.lang.System#identityHashCode()方法(不管是否重写了hashCode方法)获取了对象的唯一哈希值,该方法获取的hash值与hashCode()方法是一样的。我们可以看到两个字符串的唯一性hash值是一样的,证明字符串引用地址没有发生改变!所以在这里,我们并不是像之前那样创建了一个新的String字符串,而是真的改变了String的内容。这个代码案例进一步说明,String类的不可变指的是中value属性在栈中的引用地址不可变,而不是说常量池中array本身的数据元素不可变!也就是说String字符串的内容其实是可变的!
四. 结语
String作为Java中使用最为广泛的一个类,之所以设计为不可变,主要是出于效率与安全性方面考虑。这种设计有优点,也有缺点。
1. 不可变性的优点
1、只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串引用都可以指向池中的同一个字符串。但如果字符串是可变的,如果一个引用变量改变了字符串的值,那么其它指向这个值的变量内容也会跟着一起改变。
2、如果字符串是可变的,那么可能会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入数据库,以获得数据库的连接;或者在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象值,造成安全漏洞。
3、因为字符串是不可变的,在物理上是绝对的线程安全,所以同一个字符串实例可以被多个线程共享。由于不可变对象不可能被修改,因此能够在多线程中被任意自由访问而不导致线程安全问题,不需要多余的同步操作。即在并发场景下,多个线程同时读一个资源,并不会引发竞态条件,只有对资源进行读写才有危险。不可变对象不能被写,所以线程安全。
4、类加载器要用到字符串,不可变性也提供了安全性,以便正确的类可以被加载。譬如你想加载java.sql.Connection类,而这个值被改成了myhacked.Connection,那么会对你的数据库造成不可知的破坏。
5、因为字符串是不可变的,所以在字符串对象创建的时候hashCode()就被执行并把执行结果缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,所以字符串的处理速度要快过其它的键对象,这就是HashMap中的键往往都使用字符串的原因,当我们需要频繁读取访问任意键值对时,能够节省很多的CPU计算开销。
6、Sting的不可变性会提高执行性能和效率,基于Sting不可变,我们就可以用缓存池将String对象缓存起来,同时把一个String对象的地址赋值给多个String引用,这样可以安全保证多个变量共享同一个对象。因此,构造一万个string s = "xyz",实际上得到都是同一个字符串对象,避免了很多不必要的空间开销。
2. 不可变性的缺点
●丧失了部分灵活性。我们平时使用的大部分都是可变对象,比如内容变化时,只需要利用setValue()更新一下就可以了,不需要重新创建一个对象,但是String很难做到这一点。当然,我们完全可以使用StringBuilder来弥补这个缺点。
●脆弱的不可变性,String其实可以利用JNI或反射来改变其不可变性。
关键词:
-
当前速读:error: Your local changes to the following files would be overwritten by me
拉取代码出现error:Yourlocalchangestothefollowingfileswouldbeoverwrittenbymerge解决方案你团队其他成员修改
来源: -
Java8 教程_编程入门自学教程_菜鸟教程-免费教程分享
教程简介Java8(又称为jdk1 8)是Java语言开发的一个主要版本。Java8是oracle公司于2014年3月发布,可以看成
来源: Java基础之String字符串的底层原理,面试常见问题
当前速读:error: Your local changes to the following files would be overwritten by me
Java8 教程_编程入门自学教程_菜鸟教程-免费教程分享
天天观焦点:兰州高新区重点项目建设实现一季度开门红
中金湖北科投光谷REIT正式获批 全球信息
全球视讯!华为MateBook D14/D16今日开售:13代酷睿 4999元起
有人姓“苟”:担心孩子受嘲笑而改姓_环球新视野
天天日报丨五一假期用得着!冈本okamoto超薄安全套大促:立省60元
全球观热点:国产鼠标卷疯了 200元碾压千元国际大牌
黄海附近发生4.8级地震:上海等地有震感 焦点播报
天天热门:洞口县:刘玮到江口镇调研督导工作,与结对帮带年轻干部谈心谈话
记一次某应用虚拟化系统远程代码执行
【当前独家】JavaScript设计模式
每日速读!Pipelines
Spring Boot 项目代码混淆,实战来了,再也不用担心代码泄露了!
重庆警方破获特大制售假冒汽车安全气囊案 涉案金额逾2亿元|天天热文
世界即时:*ST蓝盾股债或被双双强退 蓝盾转债或成第一只退市可转债
【财经分析】山东高速、湖北科投两单REITs获批 常态化发行进入快车道
中大盘蓝筹股成调研重点 热点企业AI业务受机构关注
全球速讯:天涯社区已无法打开:网友掀悼念潮
《原神》开发商最新力作!米哈游《崩坏:星穹铁道》今日公测 每日报道
天天精选!卫星通信首次下放!华为nova 11系列首销:2499元起
网传眉山夜市有人偷小孩?警方:男子并非人贩,疑用假币引纠纷 正进一步调查
速看:百亿级新投资项目此起彼伏 光伏头部企业加速“垂直一体化”布局
【国际大宗商品早报】经济衰退担忧回升 原油基本金属全线下跌
长三角楼市:3月量价齐升
1台利润是安卓机10倍!iPhone翻新机全球卖爆:系统不卡 保值好|环球快播报
天天观热点:比OLED更香!苹果即将商用MicroLED技术:不烧屏
学系统集成项目管理工程师(中项)系列12_干系人管理-焦点消息
十四届全国人大常委会举行第三次委员长会议,赵乐际主持-环球观天下
千元以下投影仪最好别买!原因揭开 世界看热讯
fastbin_tcache 环球热闻
【天天新视野】说好一个价,上门又加价——透析搬家等服务收费乱象
64核心!京东发布首款ARM云主机:性能飙升60% 还便宜50%-每日热文
曝光徐州旅游踩坑网红账号被举报:账号私信已被封
领先苹果7年!华为将推出无创血糖监测智能手表:识别率超85%|天天热闻
全球焦点!6月上映!《变形金刚7:超能勇士崛起》新海报发布:汽车人集结
【环球新要闻】当事人回应吐槽软卧3男1女被狂批:很不高兴 理性一些吧
【环球新要闻】国际锐评丨“先开枪,再开口”美国陷入了怎样的怪圈?
打印机测试页打印不出来(打印机测试页) 当前时讯
环球时讯:C语言程序设计知识点总结02
clion使用 wsl 编译下, 文件名和目录名冲突问题|全球即时
铁矿石主力合约跌破700关口_天天微速讯
被千万粉网红曝光 徐州通报出租车违规营运处理情况:司机罚款1000元
特斯拉再获中东大户订单:以后出租车全是Model 3/Y?_全球即时
对话黑芝麻智能CEO:芯片公司 错两次可能就死了 环球时快讯
世界看点:为了地球 希捷一年翻新116万块硬盘:减少540吨垃圾
世界视讯!图论之存图
07.存储引擎
中诚信国际:终止启迪环境科技发展公司主体及相关债项信用等级
足球6步过人方法 环球今日报
当前头条:关于时间管理的一点建议
热消息:07 内存(中)实现内存页面初始化
【新华解读】两个新准则实施有望进一步推动保险公司高质量发展_天天资讯
2023中国医学装备展览会在重庆开幕-焦点日报
环球动态:玩家称赞《死亡岛2》画面优秀:那《GTA6》会是啥样?
暗黑4容量要求翻倍:至少90GB SSD 比魔兽18年积累还多
北大学生因差评被逼写检讨?电影《惊天救援》辟谣:不实传闻|当前热议
【世界热闻】龙源电力(00916)公布一季度业绩 归属公司权益持有人净利润为24.18亿元 同比增长7.09%
Django之路由层 (有名和无名分组 反向解析 路由分发 名称空间)_环球观速讯
资讯:分析查询语句:EXPLAIN
上海数据交易所迎来国际板
天天播报:注意!泰福泵业将于5月23日召开股东大会
人与动物和谐共处!大爷水中游泳:天降白鹭搭便车
国产操作系统UOS新版预告 全面支持Intel 13代酷睿:性能飞跃提升
世界速讯:ChatGPT:一个里程碑!
冲出黎明前的黑暗:国产芯不再当备胎-环球时讯
海外玩家疯狂期待国产大作《黑神话:悟空》:金箍棒充满神秘力量
分享总结:开源网关-应用管理篇
KMP算法学习笔记
记录-因为写不出拖拽移动效果,我恶补了一下Dom中的各种距离
每日资讯:【谷歌插件开发】获取当前网站COOKIE并上报HTTP-API
Java中为什么重写equals()也需要重写hashCode()?
抖音小店运营没有头绪?一定要掌握这些技巧!全篇详解! 当前简讯
债市日报:4月25日_环球消息
芒果TV五一狂促:会员年卡3折年内新低 仅79元
我国小行星防御首次任务计划公布:2030年对小行星动能撞击 世界播报
还买啥RTX 4070 AMD铁了心降价:16GB显存RX 6950 XT只要4749元_世界头条
豆瓣评分高达9.0!《灌篮高手》遭遇史上最严重盗摄|速读
热点评!别急着升级!火狐浏览器112稳定版出内存泄露Bug
in fear(fearitself)|全球聚焦
遵义12岁失联女孩被找到,一细节披露令人不解,嫌疑人已被抓获
世纪天鸿涨停 三个交易日机构净买入1.41亿元
国产武侠开放世界沙盒生存游戏 《侠乂行:浪迹天涯》预售:128元 环球快资讯
男子高速要求停车上厕所被拒后跳车 专家科普:极其危险 环球要闻
今日报丨铭瑄RTX 4070 iCraft OC12G瑷珈显卡评测:专为2K高画质而生 DLSS 3畅享百帧光追
天天微速讯:比亚迪盘活腾势!全新猎跑SUV腾势N7盲订7天订单破万
1亿像素仅1399元 荣耀X50i开箱图赏 世界今亮点
三年来北京为3万余名务工人员追发工资5.11亿元-世界速看料
06 内存(上)划分与组织内存-天天简讯
乌合之众再次上演,打工人将被AI一键淘汰?
谈谈持续集成,持续交付,持续部署之间的区别
记录一则ADG备库报错ORA-29771的案例|天天观速讯
全球热消息:数字孪生与元宇宙:虚拟与现实的奇妙对话
恒生指数25日收跌1.71% 互联网科技股集体走低 最资讯
山东海化: 关于会计政策变更的独立意见|焦点简讯
不怕打岔!微信推出“最近阅读”:近期阅读文章一键查
腾讯携手Unity:推出定制化实时导航3D地图|天天热消息
金属马达+双滚珠轴承!酷冷至尊莫比乌斯120 OC风扇图赏|快播报
吉林长春现实版“虎口拔牙” 医生:从业20多年首次