专栏首页一杯82年的JAVAJAVA中的String真的不可变吗

JAVA中的String真的不可变吗

String是不可变的吗?是的!真想变?也行~

我们都知道,String是不可变对象,即一旦创建,那么就不能改变它的状态。对此,我们来分析一波。

String的内部构造

小心翼翼进入String的内部,我们可以看到它是一个final类,那么没人能继承它,很好,很丁克。

然后直接看向它的灵魂,一个char数组,也是final,于是我们知道它怎么不可变了。

public final class String
    implements java.io.Serializable, Comparable<String>, 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

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    // 略...
}

创建对象

直接用常量给String变量赋值,不管在几个地方,几次,它们都是用的同一个数据。

除非new一个新的String。

public class StringTest {

    public static void main(String[] args) {
        String s1 = "abcd";
        String s2 = "abcd";
        String s3 = new String("abcd");
        String s4 = new String("abcd");
        System.out.println("两个常量赋值\t s1==s2:" + (s1 == s2));
        System.out.println("常量与新对象\t s1==s3:" + (s1 == s3));
        System.out.println("两个新对象\t s3==s4:" + (s3 == s4));
    }
}

输出

两个常量赋值    s1==s2:true
常量与新对象    s1==s3:false
两个新对象     s3==s4:false

利用反射修改值

上面的代码后面再加点东西,尝试修改s1的值。

public class StringTest {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String s1 = "abcd";
        String s2 = "abcd";
        String s3 = new String("abcd");
        String s4 = new String("abcd");
        System.out.println("两个常量赋值\t s1==s2:" + (s1 == s2));
        System.out.println("常量与新对象\t s1==s3:" + (s1 == s3));
        System.out.println("两个新对象\t s3==s4:" + (s3 == s4));

        // 通过反射得到被String藏起来的value字段
        Field f = String.class.getDeclaredField("value");

        // 让它敞开心扉
        f.setAccessible(true);

        // 获取s1的内部value数组
        char[] v = (char[]) f.get(s1);

        // 改掉它第一个字母
        v[0] = 'x';

        System.out.println("改变过后...");
        System.out.println("s1 = " + s1);
        System.out.println("s2 = " + s2);
        System.out.println("s3 = " + s3);
        System.out.println("s4 = " + s4);
        System.out.println("两个常量赋值\t s1==s2:" + (s1 == s2));
        System.out.println("常量与新对象\t s1==s3:" + (s1 == s3));
        System.out.println("两个新对象\t s3==s4:" + (s3 == s4));
    }
}

输出

两个常量赋值     s1==s2:true
常量与新对象     s1==s3:false
两个新对象       s3==s4:false
改变过后...
s1 = xbcd
s2 = xbcd
s3 = xbcd
s4 = xbcd
两个常量赋值     s1==s2:true
常量与新对象     s1==s3:false
两个新对象       s3==s4:false

总结

1、String内部的value通过反射真的可以改变 2、直接改动value会导致其它相同值的String对象也被改变(所以可以猜测底层实际上用的同一份数据?) 3、虽然值都改变了,但作为对象,4个变量的关系依然没有改变(new的两个String和其他两个依然不等) 4、这样做很危险 5、这样做很无聊


最佳阅读体验请点击文末 阅读原文

本文分享自微信公众号 - 一杯82年的JAVA(acupjava),作者:acupt

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux实践 - 创建用户

    PS: 记不住ip,所以在个人电脑的hosts文件中设置了个别名,因为用的腾讯云服务器,就叫qqcloud。

    acupt
  • 从0.5到1写个rpc框架 - 5:服务监控和管理(actuator)

    springboot项目中只要引入spring-boot-starter-actuator就可以得到一些管理服务的接口,比如停止服务,获取服务信息等。他用的并不...

    acupt
  • 从TCP的三次握手和四次挥手说起

    传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

    acupt
  • 【小家Spring】借助Springfox整合SpringBoot和Swagger(API接口神器)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    BAT的乌托邦
  • 机器学习实战

    本次很荣幸能邀请到邵宗文为我们带来《机器学习实战》主题分享。他有10多年运营开发、海量运维和架构规划经验,精通海量服务的架构设计和自动化运维建设,目前专注于大数...

    邵宗文
  • 大型网站架构推荐书籍

    学习是技术人员成长的基础,本次分享20本技术方面的书籍,这些书不是每一本都是经典,但是每一本都有其特点。以下20本大部分本人都看过,因此推荐给大家。 本次分享大...

    用户1220053
  • SFFAI分享 | 周龙:同步双向文本生成【附PPT与视频资料】

    基于双向编码的BERT在11项自然语言理解任务上取得了惊人的效果,而目前主流的自然语言生成任务(包括机器翻译,自动摘要等)仍然采用单向解码,即从左到右依次产生目...

    用户7282388
  • 小程序微信认证太麻烦?那是你没看这篇文章 | 小程序接入指南

    知晓君
  • salesforce 零基础学习(三十四)动态的Custom Label

    custom label在项目中经常用到,常用在apex class或者VF里面用来显示help text或者error message。有的时候我们需要用到的...

    用户1169343
  • 数字货币钱包安全白皮书

    区块链技术的迅速发展,使得数字货币渐渐走入的大众的视线,在2017年底,这股热潮达到顶峰,直接搅动着金融市场与科技市场,大量的数字货币交易流水催生了数字钱包开发...

    伍尚国

扫码关注云+社区

领取腾讯云代金券