一、魔术类Unsafe
首先,了解一些Unsafe这个类,这个类可以直接进行内存级别的相关操作(如分配释放内存、修改指定内存地址的值等),这里主要介绍的是用它的theUnsafe私有成员属性来修改指定内存位置的值。
流程如下:
1、 ⭐⭐⭐获取Unsafe theUnsafe属性🌙🌙🌙
1、 通过反射获取Unsafe.class的theUnsafe属性(可以用它来进行内存操作,这里主要是用它来修改指定内存位置的值)
2、 将属性访问权限放开
因为该属性是私有属性,本来外部是无法直接访问的,如果不把属性访问权限放开,则无法获取属性,在执行f.getInt(target)时,
会报错:java.lang.IllegalAccessException: Class thread.mutilthreads_sync.UnsafeTest can not access a member of class java.lang.Integer with modifiers "private final"
3、 获取静态属性值(获取静态属性值的时候,Field的get方法不需要传入具体的实例对象,只需传入null即可,因为静态属性本就不需要实例对象就可以获取)
获取到theUnsafe属性后,后续就可以用它来修改指定内存位置的值
2、 ⭐⭐⭐利用反射创建User类的实例对象user🌙🌙🌙
1、 创建类的class对象
2、 跟据类的class对象,获取类的构造方法(当有多个构造方法时,可以通过指定构造方法的参数类型来区分)
3、 定义预期值
4、 通过获取到的类的构造方法,来创建实例对象
3、 ⭐⭐⭐利用反射获取实例对象user的name属性,并利用theUnsafe直接操作内存地址进行属性值的修改🌙🌙🌙
1、 通过反射获取User类的实例对象target的成员属性name
2、 将User类的实例对象target私有成员属性的访问权限放开
不把属性访问权限放开,则无法获取属性,在执行f.getInt(target)时,
会报错:java.lang.IllegalAccessException: Class thread.mutilthreads_sync.UnsafeTest can not access a member of class java.lang.Integer with modifiers "private final"
但是,即便不放开,也可以利用theUnsafe对该私有成员属性的值进行修改,因为theUnsafe是直接在该属性值所在的内存地址上直接进行修改操作,不受jvm语言层面的访问权限的控制!
3、 获取属性当前值
4、 通过 theUnsafe获取name属性相对于对象实例的偏移量(后面就是利用这个偏移量定位到属性的内存地址的)
5、 定义新值
6、 通过 theUnsafe的compareAndSwapObject方法,进行属性值的修改操作
7、 取被unsafe修改后的属性值
4、⭐⭐⭐代码如下🌙🌙🌙:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import sun.misc.Unsafe;
public class ReflectTest {
private static Unsafe theUnsafe;
static{
try {
// 0 获取theUnsafe
// 00 通过反射获取Unsafe.class的theUnsafe属性(可以用它来进行内存操作,这里主要是用它来修改指定内存位置的值)
Field field = Unsafe.class.getDeclaredField("theUnsafe");
// 01 将属性访问权限放开
// 因为该属性是私有属性,本来外部是无法直接访问的,如果不把属性访问权限放开,则无法获取属性,在执行f.getInt(target)时,
// 会报错:java.lang.IllegalAccessException: Class thread.mutilthreads_sync.UnsafeTest can not access a member of class java.lang.Integer with modifiers "private final"
field.setAccessible(true);
// 02 获取静态属性值(获取静态属性值的时候,Field的get方法不需要传入具体的实例对象,只需传入null即可,因为静态属性本就不需要实例对象就可以获取)
// 获取到theUnsafe属性后,后续就可以用它来修改指定内存位置的值
theUnsafe = (Unsafe)field.get(null);
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
// 1 利用反射创建User类的实例对象user
// 1.1 创建类的class对象(三种方式:①Class.forName("全类名") ②类.class ③实例.getClass())
// Class cls = Class.forName("grammar.User");
Class cls = User.class;
// 1.2 跟据类的class对象,获取类的构造方法(当有多个构造方法时,可以通过指定构造方法的参数类型来区分)
Constructor ctor = cls.getDeclaredConstructor(String.class);
// 1.3 定义预期值
String exceptedValue = "constructor init name";
// 1.4 通过获取到的类的构造方法,来创建实例对象
User target = (User) ctor.newInstance(exceptedValue);
// 2 利用反射获取实例对象user的name属性,并利用theUnsafe直接操作内存地址进行属性值的修改
// 2.1 通过反射获取User类的实例对象target的成员属性name
Field f = target.getClass().getDeclaredField("name");
// 2.2 将User类的实例对象target私有成员属性的访问权限放开
// 不把属性访问权限放开,则无法获取属性,在执行f.getInt(target)时,
// 会报错:java.lang.IllegalAccessException: Class thread.mutilthreads_sync.UnsafeTest can not access a member of class java.lang.Integer with modifiers "private final"
// 但是,即便不放开,也可以利用theUnsafe对该私有成员属性的值进行修改,因为theUnsafe是直接在该属性值所在的内存地址上直接进行修改操作,不受jvm语言层面的访问权限的控制!
f.setAccessible(true);
// 2.3 获取属性当前值
String getNameValue = (String) f.get(target);
System.out.println(getNameValue);//获取user对象的name属性的值
// 2.4 通过 theUnsafe获取name属性相对于对象实例的偏移量(后面就是利用这个偏移量定位到属性的内存地址的)
int offset = (int) theUnsafe.objectFieldOffset(f);//获取
System.out.println("offset:" + offset);
// 2.5 定义新值
String newValue = "unsafe modify name";
// 2.6 通过 theUnsafe的compareAndSwapObject方法,进行属性值的修改操作
boolean result = theUnsafe.compareAndSwapObject(target, offset, exceptedValue, newValue);//当要修改的对象target的offset这个位置的当前值与预期值exceptedValue相同时,则将offset这个位置的值改为新值newValue
System.out.println("result :" + result);
// 2.7 取被unsafe修改后的属性值
System.out.println(f.get(target));
}
}
二、核心代码逻辑及思维导图
以下是文章中提到的通过反射创建类的实例对象,并使用 Unsafe
修改私有属性值的核心逻辑,转换成横向的思维导图:
通过反射和Unsafe修改属性值
1. 获取Unsafe的 theUnsafe
属性
- 通过反射获取
Unsafe.class
的theUnsafe
属性- 放开属性访问权限
- 获取静态属性值
2. 利用反射创建 User
类的实例对象 user
- 创建类的
Class
对象 - 获取类的构造方法
- 指定构造方法的参数类型
- 通过构造方法创建实例对象
- 定义预期值
3. 利用反射获取实例对象 user
的 name
属性,并使用 theUnsafe
修改属性值
- 通过反射获取
name
属性- 放开属性访问权限
- 获取属性当前值
- 通过
theUnsafe
获取属性相对于对象实例的偏移量 - 定义新值
- 使用
theUnsafe
的compareAndSwapObject
方法修改属性值- 当前值与预期值相同则修改为新值
- 获取被
Unsafe
修改后的属性值
以下是使用 Mermaid 语法绘制的横向思维导图代码:
graph LR
A[开始] --> B1[获取Unsafe的 theUnsafe属性]
A --> B2[利用反射创建User类的实例对象user]
A --> B3[利用反射获取user的name属性并修改]
B1 --> C1[通过反射获取Unsafe.class的theUnsafe属性]
C1 --> D1[放开属性访问权限]
D1 --> E1[获取静态属性值]
B2 --> C2[创建类的Class对象]
C2 --> D2[获取类的构造方法]
D2 --> E2[通过构造方法创建实例对象]
E2 --> F2[定义预期值]
B3 --> C3[通过反射获取name属性]
C3 --> D3[放开属性访问权限]
D3 --> E3[获取属性当前值]
E3 --> F3[通过theUnsafe获取偏移量]
F3 --> G3[定义新值]
G3 --> H3[使用theUnsafe的compareAndSwapObject方法修改属性值]
H3 --> I3[获取被Unsafe修改后的属性值]
I3 --> J[结束]
请注意,Mermaid 是一种基于文本的图表绘制工具,可以在支持 Mermaid 的 Markdown 编辑器中渲染成思维导图。如果你需要可视化的思维导图,可以使用支持 Mermaid 的工具来渲染上述代码。