简单理解Sting是不可变的原理

本文深入探讨了Java中String类型的不可变性原理,解释了String为何及如何设计为不可变,并通过实例展示了如何利用反射机制改变String的值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

经常看到说Sting是不可变的,可是平时String类型的对象的值进行可以变
比如

String s = "Hello World";
System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
s = "Hello World Again";
System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));

运行的结果为

值=Hello World,hashCode=-862545276
值=Hello World Again,hashCode=-2066678108

其实,这里有一个思维误区,就是不可变不是说对象引用s不可变,而是指它的值Hello World以后在内存中就存在了,且不可改变这个字面值,其他Strin对象都可以引用它。
因此s只是引用了这个常量Hello World,对象引用s是依然可以引用其他值,若要s也不可变,即不可以再引用其他值,则需要在其前面加上final关键字。

final
对于基本类型就是值不能变;
对于引用类型则是变量的地址值不可变改变,至于引用对象里面的值,则没有了约束。

其实String类里维持了一个不可变数组private final char value[];每次赋值了之后,那这个value就是指向内存中字面量值Hello World,且数组引用value由于final修饰,也就不可能再次改变引用了。

那么问题来了:
1、为什么说String是不可变的?
因为:String值创建了之后,该值就一直存在内存中了,且这个值不会被改变。
2、为什么要设计为不可变
因为,第一点可以共享,只需要建立一次,它就放入了常量池可供其他引用变量来引用。第二点是,为了安全,集合的相关操作中,经常使用String类型的值作为key,用的就是它的hashCode,我们不希望hashCode经常变来变去,没有唯一性,否则集合操作就存在风险。
3、一定不可变吗?
不一定,可以通过反射的方式,改变字面量的值。
4、可变StringBuilder、StringBuffer
因为里面维护了char[] value;,可以随时改变引用。StringBuffer加锁安全。

举例

        String s = "Hello World";
        String s2 = "Hello World";
        System.out.println("-------第一处-----");
        System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
        System.out.println(StrUtil.format("值={},hashCode={}", s2, s2.hashCode()));
        //值=Hello World,hashCode=-862545276
		//值=Hello World,hashCode=-862545276
        
        //反射s
        Field valueFieldOfString = String.class.getDeclaredField("value");
        valueFieldOfString.setAccessible(true);
        char[] value = (char[]) valueFieldOfString.get(s);
        value[5] = '_';
        System.out.println("-------第二处-----");
        System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
        System.out.println(StrUtil.format("值={},hashCode={}", s2, s2.hashCode()));
        //值=Hello_World,hashCode=-862545276
		//值=Hello_World,hashCode=-862545276
		
        //再次换一个
        s = "Hello World2";
        System.out.println("-------第三处-----");
        System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
        //值=Hello World2,hashCode=-969099730
        
        //【重点】Hello World
        s = "Hello World";
        System.out.println("-------第四处-----");
        System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
        //值=Hello_World,hashCode=-862545276
        
        //Hello_World
        s = "Hello_World";
        System.out.println("-------第五处-----");
        System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
        //值=Hello_World,hashCode=941091237

重点可以看到第4处,我们让其回到了最开始的Hello World,此时s依然只是该值的引用,但是结果却是反射之后的Hello_World,是因为String里面的数组的值通过反射变了。

这样就是说明,说不可变是指String里面的不可变数组对应的字面值建立之后就不会变了,就存在内存中。第二,也就是说不一定完全就不变了,可以使用反射来改变其值。

【完】

正在去BAT的路上修行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值