您的位置:首页 > 编程语言 > Java开发

彻底解开Java中String对象不可变的迷雾,让你不在疑惑

2016-11-28 09:37 453 查看
java也疯狂 2016-11-25 13:14

ps:我尽量写的通俗易懂一点,自己也是参考,查看了网上蛮多资料的,如果有什么不对的 ,请指正,还有如果我的文章对你有价值,请关注我。

什么是不可变对象?

看到这句话时心里默想一下什么时候不可变对象吧,看看你的答案是否跟我一样。

---------------------------------------

众所周知, 在Java中, String类是不可变的。

那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量成员变量一般包括基本数据类型 和 引用类型 ,其中基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。

什么是对象和对象引用

1、对象在内存中是一块内存区

2、引用也是一个4字节数据内存区域,里面存放了它所指向的对象的地址,通过这个地址可以访问对象.



以此图来解释,第1行在内存中创建了一个String对象“ABCabc” 和引用s,s存放了这个对象的地址。

打印结果为:

s = ABCabc

s = 123456

问题来了,为什么上述打印结果s的值会变化

搞清楚了对象和对象引用之后就简单多了, s只是一个引用,它指向了一个具体的对象,当s=“123456”; 这句代码执行过之后,又创建了一个新的对象“123456”, 而引用s重新指向了这个新的对象,原来的对象“ABCabc”还在内存中存在,并没有改变。



为什么String对象是不可变的?

文章开头说了什么是不可变对象,String 正好符合,先来看看String 类源码。

jdk1.6 的成员变量如下



JDK1.7中String类的主要成员变量就剩下了两个



由以上的代码可以看出, 在Java中String类其实就是对字符数组的封装。JDK6中, value是String封装的数组,offset是String在这个value数组中的起始位置,count是String所占的字符的个数。在JDK7中,只有一个value变量,也就是value中的所有字符都是属于String这个对象的。

程序执行了 String s = "ABCabc"; 其实是往char[]数组里边存入字符。

其中value,offset和count这三个变量都是private的,并且没有提供setValue, setOffset和setCount等公共方法来修改这些值,所以在String类的外部无法修改String。也就是说一旦初始化就不能修改, 并且在String类的外部不能访问这三个成员。此外,value,offset和count这三个变量都是final的, 也就是说在String类内部,一旦这三个值初始化了, 也不能被改变。所以可以认为String对象是不可变的了。

那么问题又来了substring, replace, replaceAll, toLowerCase为何可以改变String的值。



打印结果为:

a = ABCabc

a = aBCabc

a的值看似改变了,其实没有。注意了回忆一下之前说的对象与对象引用。a只是一个引用, 不是真正的字符串对象,在调用a.replace('A', 'a')时, 方法内部创建了一个新的String对象,并把这个心的对象重新赋给了引用a。来个有力的截图证明一下吧,String中replace方法的源码可以说明问题。



看到红色框框中没有,其实是一个新建了一个String对象。

那么String对象真的不可变吗,有没有什么办法可以改变呢?

就好像晚上在和老婆啪啪啪的时候一样,我们应该深入一点。╮(╯▽╰)╭跑题了。

从上文可知String的成员变量是private final 的,也就是初始化之后不可改变,也没有提供set方法,value 数组也是final修饰,不能只想其他地址了。那怎么办呢,有什么办法可以修改这个数组吗?

考验你java基本功的时候到了 ,亮点的时候到了,面试装B吹牛加分的时候到了,给你一分钟想下,有没有办法。然后再去看我的答案吧

—————————————————

给点提示,java中是有其他方法可以访问私有变量的。

——————答案在下面——————

—————————————————

没错,就是用反射, 可以反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。下面是实例代码:



打印结果为:

s = Hello World

s = Hello_World

ps: 还有如果我的文章对你有价值,请关注我。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: