String 真的不可变吗?

为什么 String 类不可变

  1. final修饰符: String类被声明为final,这意味着它不能被继承。因此,无法创建String的子类来修改其行为。
  2. 私有字符数组(char[]): String类内部使用私有的字符数组来存储字符串的内容。这个字符数组是final的,即它的引用不能被修改。一旦字符串被创建,它的内容就不能被更改。
  3. 不提供可变方法: String类没有提供用于修改字符串的方法。例如,没有类似于setCharAt(int index, char ch)的方法,而是提供了返回新字符串的方法,比如substring()concat()等。

有没有办法直接修改 String 对象的值而不是 重新创建一个字符串对象

其实是有的,Java 提供了反射机制是可以获取到私有的字段并且设置其字段值的。

关于Java反射的介绍

直接修改字符串的值,会重新创建一个字符串对象

java 复制代码
    public void test01() throws Exception {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] chars = null;
        String str = "hhhhh";
        chars = (char[]) value.get(str);
        // identityHashCode() 基于对象地址返回哈希码
        System.out.println("字符串地址:" + System.identityHashCode(str) +
                " 值:" + str +
                " value地址:" + System.identityHashCode(chars));
        str = "hello world";
        chars = (char[]) value.get(str);
        System.out.println("字符串地址:" + System.identityHashCode(str) +
                " 值:" + str +
                " value地址:" + System.identityHashCode(chars));
    }

// 输出
字符串地址:721748895 值:hhhhh value地址:1642534850
字符串地址:1724731843 值:hello world value地址:1305193908

使用反射直接修改字符串数组的值

java 复制代码
public void test02() throws Exception {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] chars = null;

        String str = "hello world";
        chars = (char[]) value.get(str);
        System.out.println("字符串地址:" + System.identityHashCode(str) +
                " 值:" + str +
                " value地址:" + System.identityHashCode(chars));

        for (int i = 0; i < chars.length; i++) {
            chars[i] = 'a';
        }

        chars = (char[]) value.get(str);
        System.out.println("字符串地址:" + System.identityHashCode(str) +
                " 值:" + str +
                " value地址:" + System.identityHashCode(chars));
    }
// 输出
字符串地址:721748895 值:hello world value地址:1642534850
字符串地址:721748895 值:aaaaaaaaaaa value地址:1642534850

使用反射修改字段的值

java 复制代码
    public void test03() throws Exception {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] chars = null;

        String str = "hello world";
        chars = (char[]) value.get(str);
        System.out.println("字符串地址:" + System.identityHashCode(str) +
                " 值:" + str +
                " value地址:" + System.identityHashCode(chars));

        char[] tmp = {'1','2','3'};
        value.set(str,tmp);
        chars = (char[]) value.get(str);
        System.out.println("字符串地址:" + System.identityHashCode(str) +
                " 值:" + str +
                " value地址:" + System.identityHashCode(chars));
    }
// 输出
字符串地址:721748895 值:hello world value地址:1642534850
字符串地址:721748895 值:123 value地址:1724731843

当然这种直接修改char[]数组,绕过了String类安全机制的操作是不推荐的,因为它可能导致程序在运行时产生不可预测的行为,并且可能破坏其他代码对字符串不可变性的依赖。

使用反射修改违背了 Java 语言中字符串不可变性的设计原则。在实际开发中,最好遵循这个设计原则,以确保代码的可靠性和可维护性。如果需要可变的字符串,建议使用StringBuilderStringBuffer类。

相关推荐
不会代码的小测试2 分钟前
UI自动化-POM封装
开发语言·python·selenium·自动化
roman_日积跬步-终至千里8 分钟前
【Java并发】Java 线程池实战:警惕使用CompletableFuture.supplyAsync
java·开发语言·网络
毕设源码-钟学长10 分钟前
【开题答辩全过程】以 基于Springboot的扶贫众筹平台为例,包含答辩的问题和答案
java·spring boot·后端
lsx20240613 分钟前
C++ 基本的输入输出
开发语言
CodeSheep程序羊25 分钟前
拼多多春节加班工资曝光,没几个敢给这个数的。
java·c语言·开发语言·c++·python·程序人生·职场和发展
独好紫罗兰26 分钟前
对python的再认识-基于数据结构进行-a002-列表-列表推导式
开发语言·数据结构·python
I'mChloe34 分钟前
PTO-ISA 深度解析:PyPTO 范式生成的底层指令集与 NPU 算子执行的硬件映射
c语言·开发语言
编程小白20261 小时前
从 C++ 基础到效率翻倍:Qt 开发环境搭建与Windows 神级快捷键指南
开发语言·c++·windows·qt·学习
我是咸鱼不闲呀1 小时前
力扣Hot100系列19(Java)——[动态规划]总结(上)(爬楼梯,杨辉三角,打家劫舍,完全平方数,零钱兑换)
java·leetcode·动态规划
像风一样的男人@1 小时前
python --读取psd文件
开发语言·python·深度学习