String为什么不可变?

String为什么不可变?

什么是不可变?

一个对象创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。

不能改变状态是指:不能改变对象内的成员变量,包括基本数据类型值不能改变,引用数据类型引用不能改变,引用指向的对象的状态也不能改变。

String为什么是不可变的?

这就要看jdk源码了,String源码如下:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
    ...
}

下面来解读一下,首先String类被final修饰,表明String类无法被继承,其次String底层核心字符数组char value[]也是由final修饰。final修饰的字段创建以后就不可改变。数组是引用数据类型,数组value不可变只是单纯的引用地址无法被改变,但是引用地址指向的数组对象是可变的。

数组变量只是stack上的一个引用,数组的本体结构在heap堆。String类里的value用final修饰,只是说stack里的这个叫value的引用地址不可变。没有说堆里数组本身数据不可变。

如下代码示例:

final int[] value = {1,2,3};
value[2] = 100; //这时候数组里已经是{1,2,100},而且代码并不会报错

那么String类到底为什么是不可变的呢?

原因如下:

①String由final修饰禁止继承,避免被其他人继承后破坏。②字符数组是由private和final共同修饰,final修饰表明数组引用无法被改变;private修饰表明字符数组只能在String类里使用,而且所有String的方法都没有去改变数组元素。

因此String是不可变的关键都在底层的实现,而不是一个final了得。

为什么 String 被设计成不可变的?

1、字符串常量池优化需要

字符串常量池是方法区的一个特殊的储存区。当新建一个字符串的时候,如果此字符串在常量池中早已存在,会返回一个已经存在字符串的引用,而不是新建一个对象。

如下代码只会在堆中创建唯一一个字符串对象。

String string1 = "abcd";
String string2 = "abcd";

内存图是这样的:

java-string-pool

如果String可变,那么用某个引用一旦改变了字符串的值将会导致其他引用指向错误的值。

2、缓存哈希码

字符串的哈希码在 Java 中经常使用。例如,在 HashMap 或 HashSet 中。字符串不可变保证哈希码始终相同,所以在操作的时候可以不必担心哈希码的改变。这意味着每次使用哈希码时都无需计算。这样效率更高。这也是HashMap中的key往往都使用字符串的原因。

在 String 类中,它具有以下代码:

private int hash;//this is used to cache hash code.

3、使其他对象使用更加方便

为了使其具体化,请考虑以下程序:

HashSet<String> set = new HashSet<String>();
set.add(new String("a"));
set.add(new String("b"));
set.add(new String("c"));
 
for(String a: set)
	a.value = "a";

在此示例中,如果 String 是可变的,则可以更改其值,这将违反 set 的设计(set 包含不重复的元素)。当然,上面的例子只是为了演示,String类中并没有value属性。

4、出于安全考虑

像网络地址URL,文件路径path以及用户账号密码等通常都是以字符串String类型保存,如果字符串是可变的,将会引发各种安全隐患。
比如将密码用String类型保存,那么它将一直留在内存中,直到垃圾收集器把它清除。假如String类是可变的,那么这个密码可能会被改变,导致出现安全问题。

5、线程安全

同一个字符串实例可以被多个线程共享,因为字符串不可变,本身就是线程安全的。

总之,String出于效率和安全原因,它被设计为不可变的。

参考资料:

1、https://blog.csdn.net/weixin_38004638/article/details/115012897

2、https://www.programcreek.com/2013/04/why-string-is-immutable-in-java/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不进大厂不改名二号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值