Java 反射

在本教程中,我们将学习 Java 反射,反射允许我们在运行时操作类、接口、构造函数、方法和属性字段。

在本教程中,我们将学习 Java 反射,反射允许我们在运行时操作类、接口、构造函数、方法和属性字段。

Java 中有一个名为的 Class 类,它在运行时保存有关对象和类的所有信息。 Class 类的对象可以用来执行反射。

Java 类的反射

为了反射一个 Java 类,我们首先需要创建或者获取当前类 Class 对象。

使用 Class 类,我们可以调用各种方法来获取有关类中存在的方法、字段和构造函数的信息。

有三种获取 Class 对象的方法:

  1. 使用 Class.forName() 方法

    public class Dog {...}
    
    Class a = Class.forName("Dog");
    

    在这里,forName() 方法将要反射的类的名称作为其参数。如果类在某个包内,则使用类的全名作为参数,比如: net.gobeta.Dog

  2. 使用 obj.getClass() 方法

    Dog d1 = new Dog();
    
    Class b = d1.getClass();
    

    在这里,我们在 d1 对象上调用 getClass() 方法返回 Class 对象。

  3. 使用类的静态 class 属性

    Class c = Dog.class;
    

现在我们知道了如何获取 Class 对象, 我们可以使用此对象在运行时获取有关类的信息,比如方法、属性、注解等。

示例:Java 类反射

import java.lang.Class;
import java.lang.reflect.*;

class Animal {}

final class Dog extends Animal {
  public void display() {
    System.out.println("I am a dog.");
  }
}

public class Main {
  public static void main(String[] args) {
    try {
      // 创建对象
      Dog d1 = new Dog();

      // 获取 Class 对象
      Class clazz = d1.getClass();

      // 获取类型
      String name = clazz.getName();
      System.out.println("Name: " + name);

      // 获取类的修饰符
      int modifier = clazz.getModifiers();

      // 将修饰符转为字符串
      String mod = Modifier.toString(modifier);
      System.out.println("Modifier: " + mod);

      // 获取父类
      Class superClass = clazz.getSuperclass();
      System.out.println("Superclass: " + superClass.getName());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

输出

Name: Dog
Modifier: final
Superclass: Animal

在上面的例子中,我们创建了一个父类 Animal 和一个子类 Dog,我们要获取 Dog 类的信息。注意语句:

public class clazz = d1.getClass();

通过上面的语句我们获取到 Dog 类关联的类对象。我们调用类对象的不同方法获取类的信息:

  • clazz.getName() - 返回类的名称
  • clazz.getModifiers() - 返回类的访问修饰符
  • clazz.getSuperclass() - 返回类的超类

! 注意:我们使用 Modifier 类将访问修饰符从整数转换为字符串。

反射字段、方法和构造函数

java.lang.reflect 包里有很多用于和操作类成员的类。例如:

  • Method - 封装了类的一个方法的信息
  • Field - 封装了类的一个属性字段的信息
  • Constructor - 封装了类中构造函数的信息

Java 方法的反射

Method 类提供了可用于获取存在于类中的方法信息的各种方法。例如,

import java.lang.Class;
import java.lang.reflect.*;

class Dog {

  // methods of the class
  public void display() {
    System.out.println("I am a dog.");
  }

  private void makeSound() {
    System.out.println("Bark Bark");
  }
}

public class Main {
  public static void main(String[] args) {
    try {
      Dog d1 = new Dog();

      // 获取类对象
      Class clazz = d1.getClass();

      // 获取类中所有方法的数组
      Method[] methods = clazz.getDeclaredMethods();

      // 遍历所有方法对象
      for (Method m : methods) {

        // 方法名
        System.out.println("Method Name: " + m.getName());

        // 方法修饰符
        int modifier = m.getModifiers();
        System.out.println("Modifier: " + Modifier.toString(modifier));

        // 方法返回值
        System.out.println("Return Type: " + m.getReturnType());
        System.out.println();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

输出

Method Name: display
Modifier: public
Return Type: void

Method Name: makeSound
Modifier: private
Return Type: void

在上面的例子中,我们通过以下语句获取 Dog 类中的所有的方法的数组:

Method[] methods = clazz.getDeclaredMethod();

在遍历方法数组的时候,我们打印每个 Method 对象的信息:

  • getName() - 返回方法的名称
  • getModifiers() - 以整数形式返回方法的访问修饰符
  • getReturnType() - 返回方法的返回类型

Method 类还提供了可被用于检查在运行时的方法的各种其它方法。

Java 字段的反射

Field 类提供了可用于获取存类中的属性信息的各种方法。例如,

import java.lang.Class;
import java.lang.reflect.*;

class Dog {
  public String type;
}

public class Main {
  public static void main(String[] args) {
    try {
      Dog d1 = new Dog();

      // 获取类对象
      Class clazz = d1.getClass();

      // 获取 type 属性的对象
      Field field1 = clazz.getField("type");
      // 为对象设置 type 属性的值
      field1.set(d1, "labrador");

      // 获取对象 type 属性的值
      String typeValue = (String) field1.get(d1);
      System.out.println("Value: " + typeValue);

      // 获取属性的访问修饰符
      int mod = field1.getModifiers();

      String modifier1 = Modifier.toString(mod);
      System.out.println("Modifier: " + modifier1);
      System.out.println(" ");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
import java.lang.Class;
import java.lang.reflect.*;

class Dog {
  public String type;
}

public class Main {
  public static void main(String[] args) {
    try {
      Dog d1 = new Dog();

      // 获取类对象
      Class clazz = d1.getClass();

      // 获取 type 属性的对象
      Field field1 = clazz.getField("type");
      // 为对象设置 type 属性的值
      field1.set(d1, "labrador");

      // 获取对象 type 属性的值
      String typeValue = (String) field1.get(d1);
      System.out.println("Value: " + typeValue);

      // 获取属性的访问修饰符
      int mod = field1.getModifiers();

      String modifier1 = Modifier.toString(mod);
      System.out.println("Modifier: " + modifier1);
      System.out.println(" ");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

输出

Value: labrador
Modifier: public

在上面的例子中,我们创建了一个 Dog 类,它有一个公共的字符串字段 type。 注意下面的语句:

Field field1 = clazz.getField("type");

在这里,我们从 Dog 类对应的 Class 对象中返回 type 字段对应 Field 对象。

然后我们使用了 Field 类的各种方法:

  • field1.set() - 设置字段的值
  • field1.get() - 返回字段的值
  • field1.getModifiers() - 以整数形式返回字段的值

同样,我们也可以访问和修改私有字段。但是,私有字段的反射与公共字段略有不同。下面的例子展示了如何使用反射操作私有字段:

import java.lang.Class;
import java.lang.reflect.*;

class Dog {
  private String color;
}

public class Main {
  public static void main(String[] args) {
    try {
      Dog d1 = new Dog();

      // 获取对应的类对象
      Class clazz = d1.getClass();

      // 访问私有属性
      Field field1 = clazz.getDeclaredField("color");

      // 修改私有属性的为可访问
      field1.setAccessible(true);

      // 设置属性值
      field1.set(d1, "brown");

      // 返回属性值
      String colorValue = (String) field1.get(d1);
      System.out.println("Value: " + colorValue);

      // 获取属性的修饰符
      int mod2 = field1.getModifiers();
      String modifier2 = Modifier.toString(mod2);
      System.out.println("Modifier: " + modifier2);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

输出

Value: brown
Modifier: private

在上面的例子中,我们创建了一个 Dog 类,它有一个私有字符串字段 color,注意下面的语句:

Field field1 = clazz.getDeclaredField("color");
field1.setAccessible(true);

我们先通过 clazz.getDeclaredField("color") 获取属性对应的 Field 对象,然后使用 field1.setAccessible(true) 方法设置属性为可修改。然后我们对私有字段执行各种操作。

Java 构造函数的反射

我们还可以使用 Constructor 类提供的各种方法操作类的构造函数 Constructor。例如:

import java.lang.Class;
import java.lang.reflect.*;

public class Dog {
  public Dog() {}
  private Dog(int age) {}
}

public class Main {
  public static void main(String[] args) {
    try {
      Dog d1 = new Dog();

      Class clazz = d1.getClass();

      Constructor[] constructors = clazz.getDeclaredConstructors();

      for (Constructor c : constructors) {
        System.out.println("Constructor Name: " + c.getName());

        int modifier = c.getModifiers();
        String mod = Modifier.toString(modifier);
        System.out.println("Modifier: " + mod);

        System.out.println("Parameters: " + c.getParameterCount());
        System.out.println("");
      }
    }

    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

输出

Constructor Name: Dog
Modifier: public
Parameters: 0

Constructor Name: Dog
Modifier: private
Parameters: 1

在上面的例子中,我们创建了一个 Dog 类,该类包括两个构造函数。我们使用反射来查找有 Dog 类的构造函数的信息。注意以下语句:

Constructor[] constructors = clazz.getDeclaredConstructor();

在这里,我们获取中 Dog 类所有的构造函数并将它们分配给一个 Constructor 类型数组。

遍历的时候,我们获取每个 Constructor 对象的信息。

  • c.getName() - 返回构造函数的名称
  • c.getModifiers() - 以整数形式返回构造函数的访问修饰符
  • c.getParameterCount() - 返回每个构造函数中存在的参数数量