从零开始学Java57之String字符串的底层原理_java string修改值的原理(1)

给大家的福利

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

在这里插入图片描述

因篇幅有限,仅展示部分资料

网络安全面试题

绿盟护网行动

还有大家最喜欢的黑客技术

网络安全源码合集+工具包

所有资料共282G,朋友们如果有需要全套《网络安全入门+黑客进阶学习资源包》,可以扫描下方二维码领取(如遇扫码问题,可以在评论区留言领取哦)~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


**我们先对上面的源码及其注释进行简单的解释:**



> 
> ● final:请参考第1小节对final特点的介绍;
> 
> 
> ● Serializable:用于序列化;
> 
> 
> ● Comparable:默认的比较器;
> 
> 
> ● CharSequence: 提供对字符序列进行统一、只读的操作。
> 
> 
> 


**从这一段源码及注释中,我们可以得出如下结论:**



> 
> ● String类用final关键字修饰,说明String不可被继承;
> 
> 
> ● String字符串是常量,字符串的值一旦被创建,就不能被改变;
> 
> 
> ● String字符串缓冲区支持可变字符串;
> 
> 
> ● 因为String对象是不可变的,所以它们是可以被共享的。
> 
> 
> 


#### 2.2 final修饰的value[]属性



public final class String
implements 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到底能不能变!我先给各位来一段案例代码,代码案例如下图所示。


![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c3ecd319b6ce4570b1ff3d466b77e4c3~tplv-k3u1fbpfcp-watermark.image?)


结果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”; 这句代码之后,真正的内存布局应该是下图这样的:


![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/63a634c3db7944a39949c63e6fac388d~tplv-k3u1fbpfcp-watermark.image?)


因为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” 而已,而其原来的字符串内容,是没有改变的,如下图所示。


![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e1bdf38530d042bcb9b9a0c71256370c~tplv-k3u1fbpfcp-watermark.image?)


因此,我们在以后的开发中,如果要经常修改字符串的内容,请尽量少用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. 结果剖析


上面案例的执行结果如下图所示:


![image.png](https://img-blog.csdnimg.cn/img_convert/74885242132931ced7f3ec46390b1a7e.png)


**我们可以看到,String字符串的字符数组可以通过反射进行修改,导致字符串的“内容”真的发生了变化!** 并且我们又利用底层的java.lang.System#identityHashCode()方法(不管是否重写了hashCode方法)获取了对象的唯一哈希值,该方法获取的hash值与hashCode()方法是一样的。我们可以看到两个字符串的唯一性hash值是一样的,证明字符串引用地址没有发生改变!所以在这里,我们并不是像之前那样创建了一个新的String字符串,而是真的改变了String的内容。这个代码案例进一步说明,**String类的不可变指的是中value属性在栈中的引用地址不可变,而不是说常量池中array本身的数据元素不可变!也就是说String字符串的内容其实是可变的!**


## 四. 结语


String作为Java中使用最为广泛的一个类,之所以设计为不可变,主要是出于效率与安全性方面考虑。这种设计有优点,也有缺点。


### 1. 不可变性的优点


1、**只有当字符串是不可变的,字符串池才有可能实现。** 字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串引用都可以指向池中的同一个字符串。但如果字符串是可变的,如果一个引用变量改变了字符串的值,那么其它指向这个值的变量内容也会跟着一起改变。



本人从事网路安全工作12年,曾在2个大厂工作过,安全服务、售后服务、售前、攻防比赛、安全讲师、销售经理等职位都做过,对这个行业了解比较全面。


最近遍览了各种网络安全类的文章,内容参差不齐,其中不伐有大佬倾力教学,也有各种不良机构浑水摸鱼,在收到几条私信,发现大家对一套完整的系统的网络安全从学习路线到学习资料,甚至是工具有着不小的需求。


最后,我将这部分内容融会贯通成了一套282G的网络安全资料包,所有类目条理清晰,知识点层层递进,需要的小伙伴可以点击下方小卡片领取哦!下面就开始进入正题,如何从一个萌新一步一步进入网络安全行业。


![](https://img-blog.csdnimg.cn/img_convert/311903982dea1d8a5d2c98fc271b5b41.jpeg)



### 学习路线图


 其中最为瞩目也是最为基础的就是网络安全学习路线图,这里我给大家分享一份打磨了3个月,已经更新到4.0版本的网络安全学习路线图。


相比起繁琐的文字,还是生动的视频教程更加适合零基础的同学们学习,这里也是整理了一份与上述学习路线一一对应的网络安全视频教程。


![](https://img-blog.csdnimg.cn/img_convert/1ddfaf7dc5879b1120e31fafa1ad4dc7.jpeg)


#### 网络安全工具箱


当然,当你入门之后,仅仅是视频教程已经不能满足你的需求了,你肯定需要学习各种工具的使用以及大量的实战项目,这里也分享一份**我自己整理的网络安全入门工具以及使用教程和实战。**


![](https://img-blog.csdnimg.cn/img_convert/bcd1787ce996787388468bb227d8f959.jpeg)


#### 项目实战


最后就是项目实战,这里带来的是**SRC资料&HW资料**,毕竟实战是检验真理的唯一标准嘛~


![](https://img-blog.csdnimg.cn/img_convert/35fc46df24091ce3c9a5032a9919b755.jpeg)


#### 面试题


归根结底,我们的最终目的都是为了就业,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值