String类型对象的不可变(待补充)

2 篇文章 0 订阅

谈到不可变,我们首先要先要知道什么叫做“变”。

这里的“变”就是让对象内容(实例变量)发生变化,对象都存在堆中,如果我们想让一个堆中的对象发生变化,那么我们首先必须要先访问到这个对象,那么怎么才能访问到一个对象呢?

我们需要用到栈中的引用(内存地址),但是光有引用就行了吗?举个例子,我想要拜访一个美国朋友,给他送点东西,但是我只知道他家的地址,我附近没有车,没有火车,没有飞机,也没有船,光靠走路走过去那是不现实的,也就是说光知道地址没用,我们还需要一种交通工具。java中也是这样,我们有了引用。也仅仅只是知道了对象的地址,我必须通过点操作符“.”或下标“[]”才能访问到对象。

现在我们可以访问到对象了,但是访问到了不代表你能对对象进行修改!还是用上面那个例子,我们现在到了美国朋友的家,你看他家不顺眼,想把里面的家具等都换了,但是房门是锁住的,你无法对家里的东西进行任何改动。java中如果对象的实例变量是public修饰的,那你可以直接用house.jiaju=red;进行赋值;但如果是private修饰的,那就只能用对象内部定义的方法来进行修改;如果是final修饰的,这里是个坑我们注意一下!!!!我们所说的final修饰不可变是指引用不可变,不是指对象不可变,举个例子final int[] x={1,2,3};,这里的x因为被final修饰,就只能指向{1,2,3}这个数组对象,如果让他指向别的对象的地址,如x={4,5,6},这时就会报错;但是如果你对{1,2,3}这个数组本身进行修改,如x[0]=4,这时并不会报错,也就是说final修饰的实例变量是可以修改的(例如刚才的数组),只是不能指向别的地址。

总结一下,想对对象内容进行修改,必须先访问到,然后只能通过“=”和实例方法进行修改,具体用哪个还是要看实例变量的修饰符。(public开放,private私有,final最终。。。。。更多修饰符知识可以看我博客)

好了,我们现在可以看看字符串String了,首先有一点必须知道字符串也是引用类型对象的一种,只不过它有点特殊。

先看String类的源码

看到这段源码,很显然的,我们首先明白了两件事:①String对象内部的实例变量是一个char数组 ②String类被final修饰,所以不能被继承。

现在开始正式说不可变了!根据我们上面讲的,我们要改变必须要访问,我们首先String x=“123”;,然后“x.方法”是可以做到的,也就是说我们成功通过点操作符访问到了string对象;第二步,开始找改变方法,因为源码里实例变量char数组是被final修饰的,所以用“=”进行修改(直接指向别的char数组对象)是不可能的;第三步,因为源码里实例变量char数组是被private修饰的,所以这个实例变量对除了对象本身以外的所有类都是隐藏的,只能通过该对象内部的方法进行修改,现在看看jdk里String类的实例方法有对char数组进行修改的方法吗?不用看啦,肯定没有,java设计师都告诉你不可变了,怎么可能留给你修改的方法。所以,至此,所有改变方法都被阻止了,也就是说字符串不可变!!!!

接下来说一下细节(用上边的例子):

1.我们用private修饰的话,x.value[0]=1是不可能的,这个实例变量被隐藏了,我们不可能通过点操作符进行访问到,连访问都访问不到,更不用说用“=”进行赋值了,也就是说,刚才说的第二步其实可以省略,private才是字符串不可变的主要原因。

2.这个类被final修饰了,按道理String x=“123”之后String x=“234”会报错啊?   兄弟,别傻了,这里final修饰的只是类,作用是让String类不可被继承,防止有人重写里边的方法,这个final和x这个引用一点点关系都没有。

3.我写了String x=“123”之后再写String x=“234”,两次打印出来x不一样呀,这不是说明字符串变了嘛?兄弟,你又理解错了,我们说的字符串不可变,是指堆中的这个字符串对象不变,而x这个栈中的引用,你想怎么变就怎么变,想让他指向哪个字符串随你便,当然,前提是x的前边不要加final(final String x=“123”)。


好了,我们知道了不可变的原理,现在来看看字符串不可变的好处是什么?

好处就两个字,安全!!!!!

说别的都不好理解,直接说例子就懂了:我们拿最典型的stringbuffer和String来做对比。

我们都知道hashset集合中的元素必须都是唯一的,我们用hashset就是为了这个唯一性!!下面看我往hashset里存stringbuffer和String。


我往里边存Stringbuffer,因为Stringbuffer是可变字符串,我们可以直接用append方法对集合里的元素(堆中的对象)进行修改,造成了上图的结果,破坏了hashset的唯一性,不符合我们用这个集合的本意。


用string就不同了,因为字符串无法改变,所以不会出现元素存入之后会改变的情况,保持了唯一性,很安全。


接下来还是说一下细节(还是用上边的例子):

1.第二段代码,我往字符串里传了x1,x2后,把x1指向“11”后为啥hashset里的x1不会改变呢?这还是老问题,我们往集合里存的是堆中的对象“1”,不是栈中的引用x1,x1指向哪和“1”这个对象无关。举个例子,我拿张纸记了朋友的家庭住址,现在这张是不是就指向了朋友的家?那我把纸上的地址擦掉,换成自己家的地址,这时朋友的家难道会消失掉吗?不可能的,朋友的家该在哪还在哪!


2.第二段代码,我往字符串里传了x1,x2后,为什么不像第一段代码里那样修改对象呢?大哥,这就是因为字符串的不可变性啊,java的设计师根本就没有把方法留给你,让你去修改字符串。


最后,说一下字符串的一个特性,很重要(抱歉,这块有点复杂,先了解个大概吧,以后会补充)

字符串常量池:

字符串的分配,和其他的对象分配一样,需要在堆中分配空间,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串对象,极大程度地影响程序的性能。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化:

  • 为字符串对象开辟一个字符串常量池
  • 创建字符串对象时,首先检查字符串常量池是否存在该字符串对象。
  • 如果字符串没有存在于字符串常量池中,就需要在堆和字符串常量池中分别创建两个对象。
  • 多个    相同字符串的  引用 指向同一片内存空间,即同一个对象。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值