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

简介: 本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括:1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制

一、魔术类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.classtheUnsafe 属性
    • 放开属性访问权限
    • 获取静态属性值

2. 利用反射创建 User 类的实例对象 user

  • 创建类的 Class 对象
  • 获取类的构造方法
    • 指定构造方法的参数类型
  • 通过构造方法创建实例对象
    • 定义预期值

3. 利用反射获取实例对象 username 属性,并使用 theUnsafe 修改属性值

  • 通过反射获取 name 属性
    • 放开属性访问权限
  • 获取属性当前值
  • 通过 theUnsafe 获取属性相对于对象实例的偏移量
  • 定义新值
  • 使用 theUnsafecompareAndSwapObject 方法修改属性值
    • 当前值与预期值相同则修改为新值
  • 获取被 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 的工具来渲染上述代码。

目录
相关文章
|
2月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
62 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
2月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
37 2
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1
|
2月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
28 2
|
2月前
|
存储 分布式计算 NoSQL
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
28 3
|
2月前
|
C语言 C++
C++番外篇——string类的实现
C++番外篇——string类的实现
20 0
|
2月前
|
C++ 容器
C++入门7——string类的使用-2
C++入门7——string类的使用-2
21 0
|
3月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
40 0
java基础(13)String类
|
4月前
|
API 索引
String类下常用API
String类下常用API
47 1
|
3月前
|
安全 Java
String类-知识回顾①
这篇文章回顾了Java中String类的相关知识点,包括`==`操作符和`equals()`方法的区别、String类对象的不可变性及其好处、String常量池的概念,以及String对象的加法操作。文章通过代码示例详细解释了这些概念,并探讨了使用String常量池时的一些行为。
String类-知识回顾①