通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值

首先,了解一些Unsafe这个类,这个类可以直接进行内存级别的相关操作(如分配释放内存、修改指定内存地址的值等),这里主要介绍的是用它的theUnsafe私有成员属性来修改指定内存位置的值。
流程如下:

一、 获取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属性后,后续就可以用它来修改指定内存位置的值

二、 利用反射创建User类的实例对象user

1、 创建类的class对象
2、 跟据类的class对象,获取类的构造方法(当有多个构造方法时,可以通过指定构造方法的参数类型来区分)
3、 定义预期值
4、 通过获取到的类的构造方法,来创建实例对象

三、 利用反射获取实例对象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修改后的属性值

四、代码如下:

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));
	}
}

猜你喜欢

转载自blog.csdn.net/u010425839/article/details/115431892
今日推荐