java编程细节总结(二):字符串为什么具有不可变性?

      也许在读这篇博客之前,我相信我和大家的想法都是一样的,就是,为什么字符串具有不可变性,该从哪个层次去准确回答也是一个面试难题,我们都必须好好理解并且好好记住它。


 一、什么是字符串

         我们都知道,字符串的本质是一个数组,所以字符串简而言之就是一个被封装且用于表示字符的数组。每一个字符串中的字符都是可以提取的,比如说字符串a="sadada62",这是一个含有8个字符的字符串,长度自然为8,所以a.length()输出应该是为8。(这里注意一下:a.length()指的是a的字符串的长度,a.length指的是a数组中的字符串的个数。


二、字符串为什么不可变?

         解答这个问题之前我会问你,为什么字符串可变?它的可变会带来好处吗?会给线程在运行之间带来稳定和安全吗?     

         答案是否定的。

         如果字符串里的值被任意的修改,那么运行的程序将没有达到自己想要的效果的。而在两两进程的运行过程中,很有可能进程会影响到一个进程,如果字符串是可变的,那么它将非常不安全。

         说到字符串不可变性时,必须先理解字符串在内存中的存储啊,如何分配空间的问题。

1.字符串如何分配内存空间

String a = "abc123";
String b = new String (a);
String c = a;

 

          上面的两行代码是典型的一个String的对象建立。其中,abc123放在字符串常量池里边,而字符串常量池是堆中的一个特殊区域(这里解释一下jdk1.7才把字符串常量池放到堆里,之前是在堆外的,至于为什么在jdk1.7版本后放入堆里就是jvm回收内存时可能存在bug和各种效率问题,当然大家可以查一下jdk版本升级的功能提升问题就知道了)。而a就是有着abc123这个值的地址,其中的地址是放在栈中里边的,同时如果理解成abc123被赋值是错误的,应该是对象a有一个指向abc123地址的指针,大家要弄清楚。那么b呢?它是直接指向abc123吗?肯定不是的,它是在堆中另开辟一个空间,而堆有着和a一样的指针指向常量池中的abc123值,所以我们也可以说b实例化对象a,也称深拷贝。

如下图所示:

java编程细节总结(二):字符串为什么具有不可变性?_第1张图片

    不难看出,a的指针指向字符串常量池中的"abc",a就记录"abc"的地址;当对象b实例化对象a的时候,相当于在堆中新开辟了一个new String (a)的空间,然后新的new空间在指向字符串常量池中的"abc"地址,所以说a和b的地址是不一样的,因为b是实例化a,如果大家做一个boolean判断就可以发现 b == a这个是false的。而这里把常量池和字符串常量池区别分开也是有别意的,因为字符串常量池在堆里(上面已说),而常量池又可以分为class常量池、运行时常量池等等)

 

     这里加入一个String c = a语句应该是

java编程细节总结(二):字符串为什么具有不可变性?_第2张图片

        这里就是c浅拷贝a,就是说c只拷贝了a的地址,但是c并没有拷贝a的值,和上面所述的概念原理一样,大家可以输出c.equals(a)来进行检查就知道了。

         那么说了这么多,为啥字符串具有不变性呢?

2.字符串的不可变性的原因

      综上所述,可以总结如下几点:

         (1)字符串本质是一个数组,可以说字符串是一个封装的数组。

         (2)而数组具有连续存储性质,创建数组大小,会在内存内开辟一个新的空间,这个空间同时也是连续的。

        很多人概念很模糊会问,空间连续就可以说不可变吗?如果你去买一个蛋糕,拿一个勺子去挖一小块蛋糕吃,这一小块难道还能变成碎碎的吗?只有再次进入嘴里咀嚼才会碎吧~~这就是都已经变成了一个整体,它也不会变了。所以正是因为字符串不可变,在赋值的时候如果是字符串值相同会不特意开辟另一个空间地址去存储,也就是说如果a = "hello" ,同时b也是"hello"的话,字符串hello使用的是同一个地址,所以a = b这个地址比较式子是正确的。

java编程细节总结(二):字符串为什么具有不可变性?_第3张图片

        字符串的不可变性给程序带来了许许多多的安全,让多线程的运行不轻易撰写修改数据,提高程序运行的安全性和稳定性。

 


          Thanks you~❤

你可能感兴趣的:(java编程细节总结(二):字符串为什么具有不可变性?)