Java 中的反射机制

186 阅读6分钟

1. 概念

  Java中的反射机制是指在运行时动态获取并操作类的信息的能力。通过反射,可以在运行时访问和操作对象、类、接口、方法、属性、构造函数等元素,包括私有的和受保护的元素。

2. 与反射相关的类

类名用途
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量/类的属性
Method类代表类的方法
Constructor类代表类的构造方法

3. Class 类的运用

  java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件,被编译后的.class文件也被JVM解析为一个对象,这个对象就是 java.lang.Class 。这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类。

3.1 获得 Class 对象的三种方式

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

  1. 使用对象的getClass()方法。
  2. 使用类的class属性。
  3. 使用Class类的forName()方法。

(下面的 ?为通配符)

class Student{
....
}

public class Main {
    public static void main(String[] args) {

        Student student = new Student();

        //通过 getClass() 的方法
        Class<?> c1 = student.getClass();

        //通过 .class 的方式
        Class<?> c2 = Student.class;

        //通过 forName("这里是类的相对路径") 方法,这种方法要加异常
        Class<?> c3 = null;
        try {
            c3 = Class.forName("demo.Student");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }


        //一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的
        //这三种方式获取的 class 对象都是同一个
        System.out.println(c1.equals(c2)); //true
        System.out.println(c1.equals(c3)); //true
        System.out.println(c2.equals(c3)); //true
    }
}

3.2 Class 类中的常见方法

获得类相关信息的方法:

方法用途
ClassLoader getClassLoader()获得类的加载器
Class<?>[] getDeclaredClasses()返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的)
Class<?> forName(String className)根据类名返回类的对象
Object newInstance()创建类的实例
String getName()获得类的完整路径名字

获得类中构造器相关的方法:

返回类型方法用途
public Constructor<T>getConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
public Constructor<?>[]getConstructors()获得该类的所有公有构造方法
public Constructor<T>getDeclaredConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的构造方法(包括私有的)
public Constructor<?>[]getDeclaredConstructors()获得该类所有构造方法(包括私有的)

常用获得类中属性相关的方法:

返回类型方法用途
public FieldgetField(String name)获得某个公有的属性对象
public Field[]getFields()获得所有公有的属性对象
public FieldgetDeclaredField(String name)获得某个属性对象(包括私有的)
public Field[]getDeclaredFields()获得所有属性对象(包括私有的)

获得类中方法相关的方法:

返回类型方法用途
public MethodgetMethod(String name, Class<?>... parameterTypes)获得该类某个公有的方法
public Method[]getMethods()获得该类所有公有的方法
public MethodgetDeclaredMethod(String name, Class<?>... parameterTypes)获得该类某个方法(包括私有的)
public Method[]getDeclaredMethods()获得该类所有方法(包括私有的)

获得类中注解相关的方法:

返回类型方法用途
public <T extends Annotation> TgetAnnotation(Class annotationClass)返回该类中与参数类型匹配的公有注解对象
public Annotation[]getAnnotations()返回该类所有的公有注解对象
public <T extends Annotation> TgetDeclaredAnnotation(Class annotationClass)返回该类中与参数类型匹配的所有注解对象
public Annotation[]getDeclaredAnnotations()返回该类所有的注解对象

3.3 Field 类提供的常用方法

返回类型方法名称描述
Objectget(Object obj)返回指定对象上此 Field 表示的字段的值
voidset(Object obj, Object value)将指定对象上此 Field 表示的字段设置为指定的新值
Class<?>getDeclaringClass()返回一个描述此 Field 所属类或接口的 Class 对象
StringgetName()返回 Field 的名称
Class<?>getType()返回 Field 的类型
intgetModifiers()返回此 Field 的 Java 语言修饰符
booleanisAccessible()返回此 Field 的可访问标志
voidsetAccessible(boolean flag)将此 Field 的可访问标志设置为指定的布尔值

3.4 Method 类提供的常用方法

返回类型方法名称描述
Objectinvoke(Object obj, Object... args)以指定的参数调用此方法,并返回此方法的返回值
booleanisBridge()如果此方法为桥方法,则返回 true,否则返回 false
booleanisVarArgs()如果此方法是使用可变数量的参数调用的,则返回 true,否则返回 false
Class<?>getDeclaringClass()返回一个描述此方法所属类或接口的 Class 对象
StringgetName()返回方法的名称
Class<?>[]getParameterTypes()返回一个 Class 对象数组,其中包含按声明顺序标识形参类型的类或接口
intgetParameterCount()返回方法的形参个数
Class<?>getReturnType()返回方法的返回类型
intgetModifiers()返回此方法的 Java 语言修饰符
booleanisAccessible()返回方法的可访问标志
voidsetAccessible(boolean flag)将此方法的可访问标志设置为指定的布尔值

3.5 Constructor 类提供的常用方法

返回类型方法名称描述
Constructor<T>Constructor()创建一个新的对象
TnewInstance()创建一个新的对象实例
Class<?>getDeclaringClass()返回描述此构造函数所表示的类的 Class 对象
StringgetName()返回构造函数的名称
intgetParameterCount()返回构造函数的参数个数
Class<?>[]getParameterTypes()返回一个 Class 对象数组,其中包含按声明顺序标识形参类型的类或接口
intgetModifiers()返回此构造函数的 Java 语言修饰符
booleanisAccessible()返回构造函数的可访问标志
voidsetAccessible(boolean flag)将此构造函数的可访问标志设置为指定的布尔值
TnewInstance(Object... initargs)通过使用指定的初始化参数创建新实例

3.6 简单的案例

假设现在有如下的 Student 的类:

public class Student {

    public String name;
    private int id;
    public int age;

    //无参构造方法
    public Student() {

    }

    //私有构造方法
    private Student(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    //共有方法
    public void pubMethod1() {
        System.out.println("public 的方法");
    }

    //私有方法
    private void priMethod() {
        System.out.println("private 的方法");
    }

    //有参数的方法
    private void paramMethod(String str) {
        System.out.println(str);
    }

    //有返回值的方法
    @Override
    public String toString() {
        return "demo.Student{" +"name=" + name  +", id=" + id +", age=" + age +'}';
    }
}

(1)通过反射来创建实例

// 通过反射来创建对象
public static void reflectNewInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {

    //获取一个 Class 对象
    Class<?> c1 = Class.forName("demo.Student");
    //创建一个实例
    Student student = (Student)c1.newInstance();
    student.name = "张三";
    student.age = 19;
    System.out.println("获取的Student对象:"+student.toString());
}

调用该方法后的结果:
获取的Student对象:demo.Student{name=张三, id=0, age=19}

(2)通过反射获取构造方法来创建实例

// 反射私有的构造方法
public static void reflectPrivateConstructor() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    //获取一个 Class 对象
    Class<?> c = Class.forName("demo.Student");
    //getDeclaredConstructor 的参数要对应 构造方法里的参数
    Constructor<?> constructor = c.getDeclaredConstructor(String.class,int.class,int.class);
    //设置访问,当参数为 true 的时候表示可以获取到私有构造方法
    constructor.setAccessible(true);
    //创建一个实例
    Student student = (Student) constructor.newInstance("小明",1,20);
    System.out.println(student);
}

调用该方法后的结果:
demo.Student{name=小明, id=1, age=20}

(3)反射私有属性

// 反射私有属性
public static void reflectPrivateField() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
    Class<?> c = Class.forName("demo.Student");

    //反射私有属性,
    Field field = c.getDeclaredField("id");
    field.setAccessible(true);

    //创建一个实例
    Student student = (Student) c.newInstance();
    field.set(student,20);
    System.out.println(student);
}
结果:
demo.Student{name=null, id=20, age=0}

(4)反射私有方法

// 反射私有方法
public static void reflectPrivateMethod() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
    Class<?> c = Class.forName("demo.Student");
    //获取私有方法 paramMethod
    Method method = c.getDeclaredMethod("paramMethod",String.class);
    method.setAccessible(true);

    //创建一个实例
    Student student = (Student)c.newInstance();
    //调用方法
    method.invoke(student,"我是 paramMethod() 方法!");
    
}
结果:
我是 paramMethod() 方法!