String类之所以说是无法被修改,原因在于类本身以及成员变量都被final修饰了。如下
其中可以看到,字符串是以char[ ]的形式保存在value内的。
value虽然被final修饰了,但我们知道,value是数组类型的,是一个引用,value本身只是一个地址,指向堆里真正的数组实例。我们无法修改value的地址值,但却可以获取地址后,利用地址修改数组的内部元素。
因此利用反射获取私有成员变量后,就可以达到修改String值的目的。
String s1 = "abc";
System.out.println(s1);
System.out.println(s1.hashCode());
Class aClass = s1.getClass();
try {
Field value= aClass.getDeclaredField("value");
//设置私有成员变量可访问
value.setAccessible(true);
//反射无法直接修改final修饰的成员变量
// f.set(s1, "bcd");
//获取String类value指向的地址,并赋值给o1
char[] o1 = (char[])value.get(s1);
//利用o1修改地址所指向的char数组
o1[0] = 'b';
System.out.println(s1);
System.out.println(s1.hashCode());
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
但类似的操作对Integer等其他封装类无效,因为它们的value是基础数据类型,value内保存的是实际的值,无法通过获取地址的方式,改变其值。