String底层是一个final修饰的char数组,private修饰的属性不能被外界访问,我们无法改变它,也无法影响String的值

初始化的String的地址不能改变,但是地址中的内容可以改变
这个final修饰的char[] value 不是不可变,数组是引用类型,fianl修饰的引用类型不能指向其他引用,但是本身是可以修改的
因此只要可以访问到String的value数组,就能达到修改String的目的
通过反射可以获取到String的私有属性value
public class Test {
public static void main(String[] args) {
String a = "hello";
String b = "world";
System.out.println(a+"的hashcode= "+a.hashCode());
try {
//反射获取string的私有属性value数组
Field value = a.getClass().getDeclaredField("value");
//设置value数组可以访问
value.setAccessible(true);
//获取value属性里面的值(内存地址)
char[] temp = (char[])value.get(a);
//通过temp的地址引用 找到真实String对象中的数组,修改数组内的元素
value.set(a,new char[]{'x','x','x','x','x'});
System.out.println(a+"的hashcode= "+a.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("a="+a+",b="+b);
}
}

观察输出,字符串a已经通过反射拿到value数组修改过了
还有一个问题,修改前后的 a 的hash相同。
hash值不等于地址,String的hash码是通过对每个字符进行一定规则累加得到的
可以说一个String对象的hashCode()只会执行一次,因此它的值改变后,hash码还是原来的,为什么这么说?
来看hashCode()的源码
public int hashCode() {
//拿到String的hash
int h = hash;
//hash=0就去计算这个string的hashCode码
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
//将hashCode结果赋给hash
hash = h;
}
//hash不等于0,也就是这个String对象之前已经执行过hashCode()了,直接返回hash属性
return h;
}
总结:不能用hashCode()来判断是否是同一个字符串
public class TestString {
public static void main(String[] args) {
char[] chars = new char[]{'h','e','l','l','o'};
String str = SharedSecrets.getJavaLangAccess().newStringUnsafe(chars);
StringHolder holder = new StringHolder(str);
System.out.println(holder.value);//hello
for (int i = 0; i < chars.length; i++) {
chars[i] = '?';
}
System.out.println(holder.value);//?????
for (int i = 0; i < chars.length; i++) {
chars[i] = '#';
}
System.out.println(holder.value);//#####
}
}

工作流程
