深入理解字符串的不可变性[java]

深入理解字符串的不可变性

/*

这篇文章是我认为写的最好的一篇文章,只要认真去看,我相信你一定会有收获

*/

//只要你把其中的例题做对,也就代表你掌握了

首先,我们要探讨字符串的不可变性,那么究竟什么是字符串的不可变性呐?

字符串的不可变性:

  • String类内部定义了 final char[] value ;数组用于存储字符串数据
    • 这里的value数组是通过final关键字修饰的,也就说们字符串其实是一个常量,一旦在内存中申请出一个空间并且为其赋值之后这个空间中的字符串值就不可以修改了,将这个性质我们称之为不可变性

深入理解:

不可变性的体现一:

当对字符串重新赋值时我们需要重新制定一个内存区域,然后才能赋值,不能对原有的value进行赋值(也就是我们不能改变原有的value)

  • 这里其实就是我们每当有一个对象就有一次给value赋值的机会,这次机会用了之后,也就是赋值之后,就不可以再改变了
    • 所以我们在这里说,字符串的值一旦给定就不可以再修改,一旦尝试修改,就会创建一个新的字符串对象,让这个新的字符串去接收你想要成为的值

这里我们可以举一个例子来说明

public class Demo{
    public static void main(String[] args){
        String s1="123";  //这里的赋值方式其实就是字符串的第一种创建方式,也就是通过字面量定义的方式
        s1="hello";   //这里我们尝试对s1的值进行修改,那么这里会发生什么呐?
        System.out.println(s1);
    } 
}

在上面,我们对定义好的字符串尝试进行改变,那么有没有能成功改变s1的值呐?

  • 这里我们其实我们在运行之后发现,输出的结果真的变成了hello
  • 那么我们之前不是说字符串一旦赋值后就不可以改变了,这里怎么会改变呐?
    • 其实这里的原来的字符串的值并没有改变,那这里的s1的值为什么改变了呐?其实这里是原来的字符串的值没有变,但是s1这个字符串变量的指向发生了变化,其实我们在做这一步尝试改变的操作的时候,我们计算机的内部会再产生一个字符串对象,给这个对象赋值为"hello",然后又然字符串变量s1指向这里的新产生的对象
    • 所以这里我们说呐,就是"我本将心照明月,奈何明月照沟渠呀!",我本身没有变,但是你却指向了别人

不可变性的体现二:

当我们要对现有的字符串进行连接操作时,也需要重新指定内存区域并赋值,不能是修改原有的内存(对象)的value数组(这里的value数组就是字符串在底层存储的数组)

比如说这里我们也可以举一个例子:

public class Demo2{
    public static void main(String[] args){
        String s1="123";
        s1+="456";  /*
                      这里其实就是对字符串进行了一个拼接,那么这里的拼接是对原来的s1所表示的对象进行了                     修改吗?                
                     */ 
        System.out.println(s1);
        
    }
}

这里的输出结果在我们运行之后我们可以发现这里的运行的结果为123456,那么这里的拼接操作到底是不是对原来的s1所指的字符串对象进行了修改呐?

  • 其实也不是的,这就和我们上面说的一样,这里其实我们在进行字符串的拼接操作的时候我们其实也不是对原来的s1指向的对象进行了修改,而是重新在内存中创建了一个字符串对象,并给这个新创建的字符串对象赋值为123546,然后又让s1指向了这个新创建的对象
  • 这里显然原来的字符串对象又被s1这个字符串变量(引用)给抛弃了,这个字符串变量s1又指向了新的对象了
  • 还是那句话"我本将心照明月,奈何明月照沟渠啊!"这个引用它又去指向别人了

字符串不可变性的体现三:

当我们调用String类的replace()方法来修改制定字符或者制定字符串时,也需要重新创建内存区域(对象,这里的内存区域其实就指的是对象)并且给这个新对象赋值,不能对原有的对象的value进行赋值

这里我们也可以再举一个例子:

public class Demo3{
    public static void main(String [] args){
        String s1="1213";
        s1=s1.replace("1","6"); //这里的replace()方法的返回值为String类型
        System.out.println(s1);  //想一下,这里的输出结果应该是什么?
    }
}

上面的输出结果应该是 6263,为什么是6263呐?因为我们使用s1对象调用replace()方法将s1对象中的字符1全部更换为了字符6,那么这个时候我们的s1的原有对象是不是还保持不变呐?

  • 答案是’是的’,s1指向的原来的对象还是没有改变,只是我们再调用这个replace()方法时,系统又为我们在内存中开辟了一个空间,并且将这个新的值赋给了这个新的字符串对象,然后我们后面又将这个返回值赋给了s1,也就是又让这个s1引用指向了这个新的对象,这个时候,我们的s1又抛弃了我们的原来的对象了

  • 又是那句话.脱口而出"我本将心照明月,奈何明月照沟渠啊!"

  • 别急 ,最后这里还有一道经典的关于字符串的面试题

只要你把这道题做对了,也就说明你对字符串的底层存储有一定的了解
public class Demo {
    public static void change(String s1){
        s1="1234";
    }
    public static void main(String[] args) {

        String s1="123";
        Demo.change(s1);
        System.out.println(s1);  //那么这里输出的结果应该是多少?
    }
}
  • 首先:上面的输出结果是:123
  • 为什么不是1234呐,很多人都会犯这个错误,都会认为这里返回的结果会是1234,因为很多人都认为这里通过这个change()函数传递的是主函数中变量s1的地址,所以这里我们在change()方法中改变了s1的值,然后这里就会影响到实参的值,也就是会影响到主函数中s1的值,那么真的是这样吗?
    • 显然不是这样的,那到底是为什么呐?
      • 这里我们一步一步过一遍思路.首先,我们进入到主方法中,在主方法中我们声明了一个局部变量s1,然后我们就调用了Demo类中的静态成员方法change(),我们在这方法中传递的是我们主方法中的局部变量s1,由于这里的s1是字符串,是引用类型,所以这里传递的是一个地址,我们现在假设这个地址为100,然后我们就进入到了这个成员方法中,进入这个成员方法,我们又创建了一个change()方法的局部变量s1,这个s1现在的地址也是s1,我们下一步在将这个s1对应的字符串尝试修改,这里一旦修改就出问题了,这里我们尝试修改这个字符串通过前面的知识点我们知道了,这里其实是在内存中重新创建了一个字符串对象,我们假设这个字符串对象的地址为102,那么这里我们就会让s1的对象指向这个新的对象,也就是现在的change()方法中的局部变量的s1指向的地址为102,那么我们就又回到了主方法中,回到了主方法中之后我们输出这个主方法中的s1也就是输出的地址为100的对象的值,自然输出的也就还是123
      • 所以呐,这里我们进入到这个change()方法之后其实没有对这个原有地址进行操作,所以实参,也就是主函数中的s1变量自然也就没有受到影响

总结:

String类型的对象的值一旦给定后,就不可以再改变了,如果改变了,就是创建了一个新的对象,并且将新的值赋给这个新的对象

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值