由一个道题讲起
大家都知道,String在创建后其value是不可变的,因为它的修饰符是 private final ,赋值后既不可变,比如String a = “a”; a = new String("b"),其实执行完这段代码后,原有a引用指向的内存区域存储的“a”并没有变为b,只不过是引用指向了新声明的内存区域。
那么问题来了,如果需要你改变 “a” 的值,可以实现吗?
答案是可以的。
我这里提供了两种思路,一种是绕过私有变量不可修改的限制,直接修改value[],另一种是直接修改value对应的内存区域。
通过反射修改
虽然private final修饰的变量不可修改,但是我们可以通过反射绕过java的检查,将其私有变量设为可以访问,直接修改数组的值。
public static void main(String[] args) throws Exception {
String a = "AAA";
Class clz = a.getClass();
//需要使用getDeclaredField(), getField()只能获取公共成员字段
Field field = clz.getDeclaredField("value");
field.setAccessible(true);
char[] ch =(char[])field.get(a);
ch[0] = 'B';
System.out.println(a);
}
通过Unsafe修改:
关于unsafe的使用,我在上一篇文章中已经讲过,请自行阅读。
public static void main(String[] args) throws Exception {
String a = "abc";
String b = "abc";
Unsafe unsafe = getUnsafe();
//获取a的实例变量value
Field valueInString = String.class.getDeclaredField("value");
//获取value的变量偏移值
long offset = unsafe.objectFieldOffset(valueInString);
//value本身是一个char[],要修改它元素的值,仍要获取baseOffset和indexScale
long base = unsafe.arrayBaseOffset(char[].class);
//获取value
char[] values = (char[]) unsafe.getObject(a, offset);
//通过计算偏移量为value赋值
unsafe.putChar(values, base , 'A');
System.out.println("a:"+a+" b:"+b);
//将s的值改为 abc
a = "abc";
String d = new String("abc");
System.out.println("a:"+a+" b:"+b);
System.out.println("d:"+d+" d==a:"+(d==a));
}
其原理就是通过unsafe,计算出value的偏移量,以及value的首元素地址,通过unsafe直接修改数组value第一个元素的值。
关于这两个例子其中涉及到的知识点,由于时间太晚,我会在下一篇中给出详细解读。
如果你还有什么思路,请给我留言。
◆ ◆ ◆ ◆ ◆
关注并后台回复 “面试” 或者 “视频”,
即可免费获取最新2019BAT
大厂面试题和大数据微服务视频
您的分享和支持是我更新的动力
·END·
后端开发技术
追求技术的深度