微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

String真的不可变吗?

Java中的String是不可变对象

在面向对象及函数编程语言中,不可变对象(英语:Immutable object)是一种对象,在被创造之后,它的状态就不可以被改变。至于状态可以被改变的对象,则被称为可变对象(英语:mutable object)。-- 来自百度百科

Java8 String源码

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];//Java 9已经优化为byte数组了

    /** Cache the hash code for the string */
    private int hash; // Default to 0
    
    ...
}

显然String字符串内部是使用char[]数组来存储。

而这个char[]数组是用 private final来修饰的,private就体现着面向对象的封装特性,并且String没有提供供外部访问的方法,这就意味着这个属性无法被外部访问;final则意味着这个属性无法修改,无法重新指向其他对象。且String 类没有提供/暴露修改这个字符串的方法

因此,String是不可变对象

不可变的优点

  • 线程安全。同一个字符串实例可以被多个线程共享,因为字符串不可变,本身就是线程安全的。
  • 支持hash映射。因为String的hash值经常会使用到,比如作为 Map 的键,不可变的特性也就使得hash值不会变,不需要重新计算。
  • 字符串常量池优化。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。

一定不可变吗

事实上,可以通过反射来改变String中的值

String str = "abcdef";
System.out.println("修改前的地址值:" + str + ",hash值"+ str.hashCode());
Class<? extends String> aClass = str.getClass();
Field value = aClass.getDeclaredField("value");
value.setAccessible(true);
value.set(str,"seven".getBytes());
System.out.println("修改后的地址值:" + str + ",hash值"+ str.hashCode());

显然修改后的还是同一个地址和hash值

修改前的地址值:abcdef,hash值-1424385949
修改后的地址值:seven,hash值-1424385949

查看源码可以看到,计算hashcode后hash值是由一个常量缓存下来的,所以通过反射修改后hashCode并不会变,除非进行重新计算。

注意:用反射修改String的值破坏了String的immutable特征,可能会带来各种问题,以上只是提供一个思路,不建议这么做。

不可变类都建议参考String类一样,写个变量缓存hashcode,从而防止高并发下的计算

public int hashCode() {
    int h = hash;
    if (h == 0 && !hashIsZero) {
        h = islatin1() ? Stringlatin1.hashCode(value)
                       : StringUTF16.hashCode(value);
        if (h == 0) {
            hashIsZero = true;
        } else {
            hash = h;
        }
    }
    return h;
}

关于作者

来自一线程序员Seven的探索与实践,持续学习迭代中~

本文已收录于我的个人博客https://www.seven97.top

公众号:seven97,欢迎关注~

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐