boxmoe_header_banner_img

Hello! 欢迎来到zz的小站!

加载中

文章导读

java 反射机制


avatar
zzdzz 2025年9月30日 207

参考资料:白日梦组长,迪宝

一.什么是java反射

Java 反射机制是 Java 提供的一种强大功能,它允许程序在运行时动态地获取类的结构信息(如类名、方法、字段、构造器等),并能够动态调用类中的方法或修改类的属性。反射机制为框架开发和灵活性设计提供了强大的支持。

二.java反射的核心类

Java 反射机制的核心类位于 java.lang.reflect​ 包中,主要包括以下几个类:

  1. Class: 用于表示类的本身,通过它可以获取类的结构信息(例如类的构造方法、方法、字段等)。
  2. Field: 表示类的字段,可以用来获取或修改类的字段值。
  3. Method: 表示类的方法,可以用来获取方法信息和调用方法。
  4. 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()​ 不会执行静态初始化。

示例使用场景:

  1. ​类名.class​:当你知道具体的类,且不需要动态加载时,使用这种方式。
  2. ​对象.getClass()​:当你有对象实例并希望获取该实例的类信息时。
  3. ​Class.forName()​:当你希望在运行时动态加载某个类,并且希望执行类的静态初始化时,使用这种方式。常见于反射、框架、插件等动态场景。
  4. ​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.
    }
}

代码解释

  1. 获取 Person​ 类的 Method​ 对象: Method method = clazz.getMethod("introduce");
    • 使用 getMethod()​ 获取名为 introduce​ 的实例方法。此方法没有参数,因此我们不传递任何参数类型。
  2. 调用实例方法: 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
    }
}

代码解释

  1. 获取静态方法的 Method​ 对象: Method method = clazz.getMethod("add", int.class, int.class);
    • 使用 getMethod()​ 获取静态方法 add(int, int)​,该方法接受两个整数参数。
  2. 调用静态方法: Object result = method.invoke(null, 5, 10);
    • 对于静态方法,invoke()​ 的第一个参数可以传入 null​,因为静态方法不依赖于对象实例。
    • 调用 method.invoke(null, 5, 10)​ 实际上等效于 MathUtils.add(5, 10)​,返回值 15​。



评论(已关闭)

评论已关闭