Java的String不可变字符串身份的讨论

String不可变字符串

java中的String是不可变字符串,这个是官方发布的身份证明。

Strings are constant; their values cannot be changed after they are created.
字符串是常量,他们的值在创建后不能被更改。

我们可以通过JDK的String源码中看到相关的表现

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. 该值用于字符存储*/
    private final char value[];
}

我们可以看到

  1. String的底层存储字符串使用的是char[]存储
  2. char[] value 是被private final,修饰的表示不可变的私有变量。但是我们需要注意的是,是value指向的char数组的地址不可变,但是其内容是可变的。
  3. String中没有提供直接获得char[] value 的方法。所以String的value属性对我们是不可见的。
    以上3点可以解释为什么String是不可变字符串。
但是我们真的没法改变String的值吗?

答案是可以改变的。
思路:

我们已经知道String的底层是char[] 。如果我们获得value的char[] 数组,直接修改value数组中的内容就可以了。那如何获得类中的私有属性呢?反射。

String s1 = " abcdefg ";

//获得String的 value属性
Field value = String.class.getDeclaredField("value");
//开启私有属性和方法的可读模式
value.setAccessible(true);
//获得s1 对象的 value值
char[] o = (char[]) value.get(s1);
o[0] = 's';
System.out.println(s1);

/**
*		sabcdefg 
**/

但是这样使用是很有风险的。

String s1 = " abcdefg ";
String s2 = " abcdefg ";

Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] o = (char[]) value.get(s1);
o[0] = 's';
System.out.println(s1);
System.out.println(s2);

/**
*	sabcdefg
*	sabcdefg
**/

我们知道s2我们并没有进行修改,但是输出是s2的值也发生了改变,那是因为 s1和s2的char[] 指向的地址是同一个地址。因为String声明定义是我们会现在字符串常量池(JDK1.8之后,在元空间)中常见其字符串,然后堆中对象指向该字符串。下一个String对象的值如果相同则其值也会指向该字符串。

还会发生更严重的错误:

String s1 = " abcdefg ";
String s2 = " abcdefg ";

Field value = String.class.getDeclaredField("value");
value.setAccessible(true);

char[] o = (char[]) value.get(s1);
o[0] = 's';

System.out.println(s1);
System.out.println(s2);

String s3 = " abcdefg ";
System.out.println(s3);
System.out.println(" abcdefg ");

/**
*	sabcdefg 
*	sabcdefg 
*	sabcdefg 
*	sabcdefg 
**/

我们会发现" abcdefg “的输出竟然是"sabcdefg”.这这这……。
因为字符串常量池中由对应的索引,所以" abcdefg “的索引对应的内容是"sabcdefg”,打印时打印的是内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值