大厂面试系列-String字符串为什么是不可变的?

面试题

在很多大厂面试的时候字符串相关的问题是必须被问到的问题。下面我们就来看一下字符串的不可变性。

String类型在Java编程中是非常常用的一种数据类型,在很多开发中我们需要不断的去更新去改变String类型的值,这个时候也许会有人疑惑,我们在操作String类型的数据的时候明明是进行了大量的改变,那么为什么说String类型是不可变的呢?

首先我们需要了解在Java 中所谓的不可变是如何去理解的?

在Java中不可变对象是指在对象创建完成之后其内部状态不会发生变化的对象。也就是说,一旦这个对象被赋值之后,我们既不能更新这个对象的引用,也不能改变它对应的值。

也许会有人问例如下面这个操作不就是对String类型的对象做了改变么?

String  str = “hello”
str = str.concat("world")

通过上面这个操作我们得到的str字符串不就是变成了“helloworld”了么?

所以说,在我们进行上面代码中的操作的时候,内容虽然从hello变成了helloworld但是实际上是创建了一个新的对象。

因为一旦一个String对象被创建,它就一定是不能被修改的,而且String类中的所有对于字符串的修改方法都不是改变对应字符串本身的值,而是返回了一个新的对象。也就是说如果我们想去创建一个可变的字符串通过String类型是肯定不行的。所以一般在使用的过程中我们创建可变字符串都是通过StringBuffer或者是StringBuilder来代替String类型对象的。

String类型为什么是不可变的呢?

通过上面的分析我们知道了String类型是不可变的,这样做的好处是什么?

String类型设计成不可变其实主要有如下几个原因

  • 缓存
  • 安全性
  • 线程安全
  • 性能

缓存

首先从缓存的角度来讲,String类型是在编程中最被广泛使用的数据类型,而且在C语言或者是C++语言中对于字符串的操作也是一个非常消耗资源的操作。所以在Java语言设计的时候,采用了缓存操作,这样可以大大提升了堆内存空间使用率。

在JVM中也专门为字符串类型开辟了存储空间,就是字符串常量池。通过这个操作,如果出现两个内容相同的字符串变量,则可以指向字符串常量池中的同一个内存地址,这样就节省了很大的空间

所以只有在字符串不变的情况下这样的操作才可以被广泛使用,如果字符串是可变的,那么这样的操作是不可以使用的。

安全性

在我们使用Java语言开发应用的时候通常将一些重要的信息使用字符类型进行存储,而且如果查看过JVM的类加载过程,我们在类加载过程中对于类的全路径都是通过字符串进行存储的。所以对于String类型安全性的提升是非常至关重要的操作。

也就是说在我们操作一个字符串的过程中如果这个字符串是不可变的,在操作过程中我们就可以完全认为这个字符串就是原来那个字符串。但是如果字符串是可变的,那么我们很有可能会在操作过程中对字符串信息进行修改,这样就导致最终获取到数据不是所要操作的数据信息。

线程安全性

不可变的共享String会是线程安全的,因为如果在多个线程共同访问的时候,线程对它的操作都是不会改变它的值,所以就形成了天然的线程安全。

所以一般来讲,如果一个不可变的对象在线程之间共享的时候,那么这个不可变对象一定是线程安全的,如果其中有一个线程改变了对象的值,那么在字符串常量池中就会出现一个新的字符串,而不是有一个相同的值对象,所以,这样来看字符串操作在多线程中是线程安全的。

HashCode识别

在很多情况下在我们使用Map这样的存储结构的时候,会将Key的值设置为String类型的对象。在对这些对象操作的时候,需要调用到hashCode()计算对象的哈希值,由于字符串的不可变性,就保证了字符串的值是不会发生变化的,所以当hashCode()方法在String类型中进行了重写代码如下。

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

这样一来,第一次调用之后,和后来调用的时候通过计算得到的哈希值都是一样的,也就保证了唯一性。

性能方面

在性能方面,无论是字符串常量池、以及使用缓存等操作都是性能的体现。这里就不再过多地说明了。

总结

通过上面的结论,我们可以知道String类型被final标记,所以从源码层面上是不可变的,字符串的引用可以看做一个普通变量,但是对于字符串本身来讲是不可变的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值