java string不可更改_Java 中的 String 真的是不可变的吗?

我们都知道 Java 中的 String 类的设计是不可变的,来看下 String 类的源码。

public final class Stringimplements java.io.Serializable, Comparable, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 // ...}

可以看出 String 类是 final 类型的,String 不能被继承。其值 value 也就是对字符数组的封装,即 char[],其值被定义成 private final 的,说明不能通过外界修改,即不可变。

为何不可变呢?

字符串常量池( String pool, String intern pool, String 保留池)

是 Java 堆内存中一个特殊的存储区域,当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。

如下面的代码所示,将会在堆内存中只创建一个实际 String 对象。

Stringstring1="abcd";

Stringstring2="abcd";

示意图如下所示:

1307608e902b8437ffd8cf6a02dd96e3.png

假若字符串对象允许改变,那么将会导致各种逻辑错误,比如改变一个对象会影响到另一个独立对象。

严格来说,这种常量池的思想,是一种优化手段。

请思考:假若代码如下所示,s1 和 s2 还会指向同一个实际的 String 对象吗?

HashSet set = new HashSet();

set.add(new String("a"));

set.add(new String("b"));

set.add(new String("c"));

for(String a: set)

a.value = "a";

也许这个问题违反新手的直觉,但是考虑到现代编译器会进行常规的优化,所以他们都会指向常量池中的同一个对象。或者,你可以用 jd-gui 之类的工具查看一下编译后的 class 文件。

Java 中 String 对象的哈希码被频繁地使用,比如在 hashMap 等容器中。字符串不变性保证了 hash 码的唯一性,因此可以放心地进行缓存。这也是一种性能优化手段,意味着不必每次都去计算新的哈希码.。

在 String 类的定义中有如下代码:

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

上面的例子肯定是不可变的,下面这个就尴尬了。

String str = "Hello Python";System.out.println(str); // Hello PythonField field = String.class.getDeclaredField("value");field.setAccessible(true);char[] value = (char[])field.get(str);value[6] = 'J';value[7] = 'a';value[8] = 'v';value[9] = 'a';value[10] = '!';value[11] = '!';System.out.println(str); // Hello Java!!

通过反射,我们改变了底层的字符数组的值,实现了字符串的 “不可变” 性,这是一种骚操作,不建议这么使用,违反了 Java 对 String 类的不可变设计原则,会造成一些安全问题。

是不是又涨姿势了?分享给你的朋友们吧!

0dc31ff0f97b742c85599b6312899f26.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值