主页 > 互联网  > 

Java反射机制深度解析:类信息的来源、declared的区别、赋值操作及暴力反射

Java反射机制深度解析:类信息的来源、declared的区别、赋值操作及暴力反射

在 Java 开发中,反射机制是一个强大且灵活的工具,它允许程序在运行时动态地获取类的信息、创建对象、调用方法和访问字段等。本文将结合代码示例和图示,深入探讨以下四个问题:

类信息来自哪里?

获取类信息时加不加 declared 的区别是什么?

如何使用类信息进行赋值操作?

什么是暴力反射?如何使用暴力反射?

一、类信息来自哪里?

类信息的来源可以分为以下几个阶段:

源码阶段:开发者编写的 .java 文件,定义了类的结构,包括字段、方法和构造器等。

编译阶段:通过 javac 编译器将 .java 文件编译为 .class 文件,类的信息被存储在字节码文件中。

加载阶段:JVM 的类加载器(ClassLoader)将 .class 文件加载到内存中,并生成对应的 Class 对象。

运行阶段:通过反射机制,程序可以动态获取 Class 对象中的字段、方法和构造器等信息。

1. 获取 Class 对象的三种方式

在 Java 中,可以通过以下三种方式获取 Class 对象:

通过全类名获取:

Class clazz1 = Class.forName("com.gcby.Student");

通过类的 .class 属性获取:

Class clazz2 = Student.class;

通过实例的 .getClass() 方法获取:

Student student = new Student(); Class clazz3 = student.getClass(); 2. 三种方式的等价性

通过上述三种方式获取的 Class 对象是同一个对象,可以使用 == 进行比较:

System.out.println(clazz1 == clazz2); // true System.out.println(clazz1 == clazz3); // true 3. 类信息的加载过程

类信息的加载过程如下图所示:

源码文件:xxx.java。

编译生成字节码文件:xxx.class。

类加载器加载字节码文件:生成 Class 对象,包含字段、方法和构造器等信息。

运行时访问类信息:通过反射机制动态获取类信息。

二、Student 类的定义

为了更好地理解反射机制,我们先来看一下 Student 类的定义:

package com.gcby; public class Student { private String name; // 私有字段 public int age; // 公共字段 double height; // 默认访问权限字段 protected char sex; // 受保护字段 // 私有方法 private void run() { System.out.println("Running..."); } // 公共方法 public int getAge() { return this.age; } // 默认访问权限方法 String getNameString(String name) { return name; } // 受保护方法 protected void run(String a, int b) { System.out.println(a + " " + b); } // 构造器 public Student(String name, int age, double height, char sex) { this.name = name; this.age = age; this.height = height; this.sex = sex; } public Student(double height, char sex) { this.height = height; this.sex = sex; } } 三、获取类信息时加不加 declared 的区别

在使用反射机制获取类信息时,getDeclaredFields()、getDeclaredMethods() 和 getDeclaredConstructors() 方法与 getFields()、getMethods() 和 getConstructors() 方法有显著区别:

getDeclared 方法:

获取当前类中声明的所有字段、方法或构造器,包括私有(private)、受保护(protected)、默认(包访问权限)和公共(public)的成员。

不会获取父类的成员。

get 方法:

获取当前类及其父类中所有公共(public)的字段、方法或构造器。

不会获取非公共成员。

1. 示例代码 获取字段 Field[] fields = clazz.getDeclaredFields(); // 获取所有声明的字段 Field[] publicFields = clazz.getFields(); // 获取所有公共字段 获取方法 Method[] methods = clazz.getDeclaredMethods(); // 获取所有声明的方法 Method[] publicMethods = clazz.getMethods(); // 获取所有公共方法 获取构造器 Constructor[] constructors = clazz.getDeclaredConstructors(); // 获取所有声明的构造器 Constructor[] publicConstructors = clazz.getConstructors(); // 获取所有公共构造器 2. 示例输出

假设 Student 类如下:

public class Student { private String name; public int age; double height; protected char sex; private void run() {} public int getAge() { return age; } } 使用 getDeclaredFields(): Field[] fields = clazz.getDeclaredFields(); System.out.println(Arrays.toString(fields)); // 输出: // [field Student.name, field Student.age, field Student.height, field Student.sex] 使用 getFields(): Field[] publicFields = clazz.getFields(); System.out.println(Arrays.toString(publicFields)); // 输出: // [field Student.age] 使用 getDeclaredMethods(): Method[] methods = clazz.getDeclaredMethods(); System.out.println(Arrays.toString(methods)); // 输出: // [method void Student.run(), method int Student.getAge()] 使用 getMethods(): Method[] publicMethods = clazz.getMethods(); System.out.println(Arrays.toString(publicMethods)); // 输出: // [method int Student.getAge()] 四、如何使用类信息进行赋值

通过反射机制,可以动态地为对象的字段赋值,即使字段是私有的。以下是具体步骤:

获取类对象:

Class clazz = Class.forName("com.gcby.Student");

创建实例:

Constructor constructor = clazz.getConstructor(); Student student = (Student) constructor.newInstance();

获取字段并赋值:

Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); // 忽略访问权限修饰符 nameField.set(student, "张三");

调用方法:

Method runMethod = clazz.getDeclaredMethod("run"); runMethod.setAccessible(true); runMethod.invoke(student); 1. 示例代码

完整代码如下:

import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; public class Test { public static void main(String[] args) throws Exception { Class clazz = Class.forName("com.gcby.Student"); // 创建实例 Constructor constructor = clazz.getConstructor(); Student student = (Student) constructor.newInstance(); // 获取字段并赋值 Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); nameField.set(student, "张三"); Field ageField = clazz.getDeclaredField("age"); ageField.setAccessible(true); ageField.set(student, 18); Field heightField = clazz.getDeclaredField("height"); heightField.setAccessible(true); heightField.set(student, 185.5); Field sexField = clazz.getDeclaredField("sex"); sexField.setAccessible(true); sexField.set(student, '男'); // 调用方法 Method runMethod = clazz.getDeclaredMethod("run"); runMethod.setAccessible(true); runMethod.invoke(student); Method getAgeMethod = clazz.getDeclaredMethod("getAge"); System.out.println("Age: " + getAgeMethod.invoke(student)); // 输出:Age: 18 } } 2. 输出结果 Running... Age: 18 五、什么是暴力反射?

暴力反射(也称为“强制反射”)是指通过反射机制绕过 Java 的访问控制限制,访问或修改类的私有成员。在 Java 中,私有成员(如私有字段和私有方法)在默认情况下是不可访问的,但通过反射机制可以强制访问这些成员。

1. 暴力反射的原理

暴力反射的核心在于 setAccessible(true) 方法。该方法可以绕过 Java 的访问控制限制,允许访问或修改私有成员。以下是具体步骤:

获取类对象:

Class clazz = Class.forName("com.gcby.Student");

获取私有字段或方法:

Field privateField = clazz.getDeclaredField("name"); Method privateMethod = clazz.getDeclaredMethod("run");

设置访问权限:

privateField.setAccessible(true); privateMethod.setAccessible(true);

访问或修改私有成员:

privateField.set(student, "张三"); privateMethod.invoke(student); 2. 示例代码

完整代码如下:

import java.lang.reflect.Field; import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws Exception { Class clazz = Class.forName("com.gcby.Student"); // 创建实例 Student student = (Student) clazz.newInstance(); // 获取私有字段并赋值 Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); nameField.set(student, "张三"); // 获取私有方法并调用 Method runMethod = clazz.getDeclaredMethod("run"); runMethod.setAccessible(true); runMethod.invoke(student); // 输出结果 System.out.println("Name: " + nameField.get(student)); // 输出:Name: 张三 } } 3. 输出结果 Running... Name: 张三 六、总结

类信息的来源:类信息从源码文件经过编译生成字节码文件,再由类加载器加载到内存中,生成 Class 对象。

getDeclared 与 get 的区别:getDeclared 获取当前类中声明的所有成员,包括私有成员;get 只获取公共成员,包括父类的公共成员。

使用类信息赋值:通过反射机制,可以动态获取字段并赋值,即使字段是私有的,也可以通过 setAccessible(true) 忽略访问权限修饰符。

暴力反射:暴力反射通过 setAccessible(true) 方法绕过 Java 的访问控制限制,允许访问或修改私有成员。虽然暴力反射在某些场景下非常有用,但在实际开发中应谨慎使用,避免破坏类的封装性和安全性。

反射机制是 Java 中的强大工具,但在实际开发中应谨慎使用,避免破坏封装性导致代码难以维护。

标签:

Java反射机制深度解析:类信息的来源、declared的区别、赋值操作及暴力反射由讯客互联互联网栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Java反射机制深度解析:类信息的来源、declared的区别、赋值操作及暴力反射