java 反射机制
参考资料:白日梦组长,迪宝
一.什么是java反射
Java 反射机制是 Java 提供的一种强大功能,它允许程序在运行时动态地获取类的结构信息(如类名、方法、字段、构造器等),并能够动态调用类中的方法或修改类的属性。反射机制为框架开发和灵活性设计提供了强大的支持。
二.java反射的核心类
Java 反射机制的核心类位于 java.lang.reflect 包中,主要包括以下几个类:
- Class: 用于表示类的本身,通过它可以获取类的结构信息(例如类的构造方法、方法、字段等)。
- Field: 表示类的字段,可以用来获取或修改类的字段值。
- Method: 表示类的方法,可以用来获取方法信息和调用方法。
- Constructor: 表示类的构造器,可以用来获取类的构造方法并实例化对象。
三.具体演示
测试类
随便写一个类即可,写一些字段和方法。
package com;
public class User {
public String name = "zzdzz";
public int age = 21;
private String address = "hangzhou";
protected String gender = "male";
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public void setAddress(String address){
this.address = address;
}
public String getAddress(){
return address;
}
public void setGender(String gender){
this.gender = gender;
}
public String getGender(){
return gender;
}
public User(){
super();
}
public User(String name, int age, String address, String gender){
this.name = name;
this.age = age;
this.address = address;
this.gender = gender;
}
private User(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
}
获取class
展示四种获取class的方法
import com.User;
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//1、根据类名:类名.class
Class<?> aclass1 = Class.forName("com.User");
System.out.println(aclass1);
//2、根据对象:对象.getClass()
User user = new User();
Class<? extends User> aClass2 = user.getClass();
System.out.println(aClass2);
//3、根据全限定类名:Class.forName("全路径类名")
Class aclass3 = Class.forName("com.User");
System.out.println(aclass3);
//4、通过类加载器获得Class对象
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class<?> aClass4 = systemClassLoader.loadClass("com.User");
System.out.println(aClass4);
}
}
总结对比:
| 获取方式 | 是否依赖实例化对象 | 是否动态加载类 | 是否执行静态初始化代码 |
|---|
| 类名.class | 否 | 否 | 是 |
| 对象.getClass() | 是 | 否 | 是 |
| Class.forName(“全路径类名”) | 否 | 是 | 是 |
| ClassLoader.loadClass() | 否 | 是 | 否 |
- 类名.class 和 对象.getClass() 是在编译时就已知的方式,通常用于静态情况。
- Class.forName() 和 ClassLoader.loadClass() 是动态加载类的方式,通常用于运行时不确定的类。
- Class.forName() 会执行类的静态初始化(例如静态代码块),而 ClassLoader.loadClass() 不会执行静态初始化。
示例使用场景:
- 类名.class:当你知道具体的类,且不需要动态加载时,使用这种方式。
- 对象.getClass():当你有对象实例并希望获取该实例的类信息时。
- Class.forName():当你希望在运行时动态加载某个类,并且希望执行类的静态初始化时,使用这种方式。常见于反射、框架、插件等动态场景。
- ClassLoader.loadClass():当你希望使用类加载器动态加载类,但不希望执行静态初始化(例如类加载时进行延迟初始化)时,使用这种方式。
获取Field(成员变量)
一共有四个方法,因为带Declared能完全替代不带的作用,这边只写带Declared的方法。
import com.User;
import java.lang.reflect.Field;
public class GetFiled {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
//先获取类
User user = new User();
Class<? extends User> aClass = user.getClass();
//获取所有成员变量
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//获取单个成员变量,参数为获取的变量名
Field filed = aClass.getDeclaredField("address");
//打开开关后可修改值内容
filed.setAccessible(true);
Object o = filed.get(user);
System.out.println(o);
//set方法修改值
filed.set(user,"zhejiang");
System.out.println(filed.get(user));
}
}
获取方法
实现反射调用方法执行
import com.User;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class GetMethod {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 创建 User 类的对象,获取 user 对象的 Class 对象
User user = new User();
Class<? extends User> aClass = user.getClass();
// 获取该类的所有声明的方法(包括私有方法、公共方法等)
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
// 获取 `setName` 方法,它接受一个 `String` 类型的参数
Method setname = aClass.getDeclaredMethod("setName", String.class);
System.out.println(setname); // 打印 `setName` 方法的信息
// 设置 `setName` 方法为可访问,允许访问私有方法
setname.setAccessible(true);
// 通过反射调用 `setName` 方法,传入参数 `"qazqaz"`
setname.invoke(user, "qazqaz"); // 这个方法调用相当于 user.setName("qazqaz");
// 打印 `user` 对象的 `getName` 方法的返回值,验证 `setName` 方法是否成功修改了 `name`
System.out.println(user.getName());
}
}
获取构造方法
import com.User;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class GetConstructor {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
User user = new User();
Class clazz = user.getClass();
// 获取该类的所有声明的构造方法(包括私有构造方法、公共构造方法等)
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
// 获取一个具体的构造方法,参数类型为 String, int, String, String
Constructor constructor = clazz.getConstructor(String.class, int.class, String.class, String.class);
// 打印出获取到的构造方法的详细信息
System.out.println(constructor);
// 设置构造方法为可访问,允许访问私有构造方法
constructor.setAccessible(true);
// 使用反射调用构造方法,传入相应的参数,创建一个 User 对象
User uu = (User) constructor.newInstance("zz", 18, "beijing", "boy");
// 打印新创建的 User 对象的属性值,验证构造方法是否正确执行
System.out.println(uu.getName());
System.out.println(uu.getAge());
System.out.println(uu.getAddress());
}
}
四.演示反射写法命令执行
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestCalc {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// Runtime.getRuntime().exec("calc");
Class<?> bclass = Class.forName("java.lang.Runtime");
Method getRuntimeMethod = bclass.getMethod("getRuntime");
//重要:需要先调用一遍方法并获取实例(因为invoke需要一个实例,Runtime类的特殊性,不能new,同时exec 是实例方法,所以必须先获取 Runtime 的唯一实例,再用这个实例调用 exec)
Object runtimeInstance = getRuntimeMethod.invoke(null);
//然后接着获取方法
Method execMethod = bclass.getMethod("exec", String.class);
execMethod.invoke(runtimeInstance,"calc");
}
}
有一个小坑点,我在注释中有初步解释,可以来看看ai的解释
为什么 getRuntime() 调用需要先获取实例:
- Runtime 类的 getRuntime() 方法是静态方法,它返回的是唯一的 Runtime 实例。由于 exec() 是 Runtime 类的实例方法,所以必须先通过 getRuntime() 方法获取到 Runtime 的实例。
- Runtime 类本身不能直接使用 new 关键字创建实例,因此我们必须通过 getRuntime() 方法来获取它的唯一实例。
invoke方法
invoke() 方法是 Java 反射机制中的一个核心方法,它用于通过反射动态地调用类的实例方法或静态方法。invoke() 方法属于 Method 类,允许你在运行时动态地调用已知方法,并传递参数。
invoke() 方法的定义
invoke() 方法定义在 java.lang.reflect.Method 类中,基本形式如下:
public Object invoke(Object obj, Object... args) throws IllegalAccessException, InvocationTargetException
参数解释
- obj: 被调用方法的对象实例。如果是静态方法,可以传入 null。如果是实例方法,则需要传入该实例对象。
- args: 被调用方法的参数,参数类型与方法的参数顺序需要一致。你可以传入一个参数数组,数组中的每个元素就是方法的一个参数。
返回值
- invoke() 方法返回调用方法后的结果。如果被调用的方法有返回值,那么 invoke() 返回该返回值。如果方法是 void 类型的,则返回 null。
示例:动态调用实例方法
下面是通过反射使用 invoke() 方法调用实例方法的示例:
import java.lang.reflect.Method;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void introduce() {
System.out.println("Hi, I'm " + name + " and I'm " + age + " years old.");
}
public String getName() {
return name;
}
}
public class InvokeExample {
public static void main(String[] args) throws Exception {
// 创建一个 Person 对象
Person person = new Person("Alice", 30);
// 获取 Person 类的 Class 对象
Class<?> clazz = person.getClass();
// 获取实例方法 `introduce()` 的 Method 对象
Method method = clazz.getMethod("introduce");
// 通过反射调用实例方法 `introduce()`,传入 person 对象作为参数
method.invoke(person); // 输出: Hi, I'm Alice and I'm 30 years old.
}
}
代码解释
- 获取 Person 类的 Method 对象:
Method method = clazz.getMethod("introduce");
- 使用 getMethod() 获取名为 introduce 的实例方法。此方法没有参数,因此我们不传递任何参数类型。
- 调用实例方法:
method.invoke(person);
- 使用 invoke() 动态地调用 introduce 方法,并传递 person 对象作为第一个参数。
- invoke() 方法会执行 person.introduce(),并输出 Hi, I’m Alice and I’m 30 years old.。
示例:动态调用静态方法
下面是通过反射使用 invoke() 方法调用静态方法的示例:
class MathUtils {
public static int add(int a, int b) {
return a + b;
}
}
public class StaticInvokeExample {
public static void main(String[] args) throws Exception {
// 获取 MathUtils 类的 Class 对象
Class<?> clazz = MathUtils.class;
// 获取静态方法 `add()` 的 Method 对象
Method method = clazz.getMethod("add", int.class, int.class);
// 使用反射调用静态方法 `add()`,传入 5 和 10 作为参数
Object result = method.invoke(null, 5, 10); // 第一个参数为 null,因为这是静态方法
System.out.println(result); // 输出: 15
}
}
代码解释
- 获取静态方法的 Method 对象:
Method method = clazz.getMethod("add", int.class, int.class);
- 使用 getMethod() 获取静态方法 add(int, int),该方法接受两个整数参数。
- 调用静态方法:
Object result = method.invoke(null, 5, 10);
- 对于静态方法,invoke() 的第一个参数可以传入 null,因为静态方法不依赖于对象实例。
- 调用 method.invoke(null, 5, 10) 实际上等效于 MathUtils.add(5, 10),返回值 15。
评论(已关闭)
评论已关闭