马士兵java架构师

您现在的位置是:架构师问答 >

架构师问答

java的反射机制是怎么实现的?

2023-11-13 20:16:25架构师问答 本文浏览次数:1 百度已收录

本 文 目 录

想象一下,你有一盒未知的乐高积木。你不知道里面有什么形状、颜色和大小的积木块,但你仍然可以在黑暗中摸索,并尝试组装它们。Java的反射机制就像是这样。

在Java中,每个类都被视为一个“对象”,而这个对象的“描述”就是Class对象。反射机制允许你在运行时查看和操作这个Class对象,可以查看类的所有方法和字段,甚至可以调用任意方法、修改任意字段。并且在运行时才能确定究竟要执行哪个操作,而不是在编译时确定。

Java反射的核心类与方法

Class类:反射的核心,获得类的Class对象后,就可以对它进行解剖。
java.lang.reflect包下的Method、Field、Constructor等类:这些类提供了用于反射操作的方法。

如何获取Class对象

获取Class对象有三种方法:
使用.class语法:Class<?> clazz = String.class;
使用Class.forName()方法:Class<?> clazz = Class.forName("java.lang.String");,这通常用于加载数据库驱动。
使用对象的.getClass()方法:String str = "hello"; Class<?> clazz = str.getClass();

通过一个简单的例子,逐行解释反射机制

首先,我们假设有如下一个简单的类:

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

下面,我们通过反射来创建Person对象并调用其方法:

public class ReflectionTest {
    public static void main(String[] args) throws Exception {
        // 获取Person类的Class对象。Class对象是反射的入口。
        Class<Person> personClass = Person.class;

        // 通过Class对象获取构造方法。这里我们知道有一个参数为String的构造方法,所以这么写。
        Constructor<Person> constructor = personClass.getConstructor(String.class);

        // 通过构造方法创建Person对象。相当于执行了"new Person("Alice")"
        Person person = constructor.newInstance("Alice");

        // 获取name方法的Method对象。注意这里获取的是公有方法。
        Method method = personClass.getMethod("getName");

        // 调用method所表示的方法,并获取返回值。相当于执行了"person.getName()"
        String name = (String) method.invoke(person);

        System.out.println(name);  // 输出:Alice
    }
}
现在,我们逐行解释上述反射代码:

1. `Class<Person> personClass = Person.class;` 获取`Person`类的`Class`对象。每个类都有一个与之对应的`Class`对象。这是反射的起点。
2. `Constructor<Person> constructor = personClass.getConstructor(String.class);` 通过`Class`对象获取`Person`类的构造方法。这里我们知道有一个接受`String`参数的构造方法。
3. `Person person = constructor.newInstance("Alice");` 使用上面获取的构造方法创建一个新的`Person`对象。这等同于使用`new Person("Alice")`进行创建。
4. `Method method = personClass.getMethod("getName");` 获取`Person`类的名为`getName`的公有方法。注意这里我们只能获取到公有方法。如果要获取私有方法,需要使用`getDeclaredMethod`。
5. `String name = (String) method.invoke(person);` 调用上面获取的`getName`方法,并获取其返回值。这等同于调用`person.getName()`。
6. `System.out.println(name);` 输出结果。因为我们之前创建的对象名为"Alice",所以输出结果为"Alice"。

Constructor constructor = personClass.getConstructor(String.class);这个是怎么来的?

`Constructor<Person> constructor = personClass.getConstructor(String.class);` 这一行代码是通过 Java 的反射 API 来获取 `Person` 类的构造函数的引用。

下面,我将详细解释这行代码的各个部分:

* `personClass`: 这是一个 `Class` 对象,它代表了 `Person` 类。我们通过 `Person.class` 获取了这个对象。
* `getConstructor(String.class)`: 这是调用 `Class` 类的 `getConstructor` 方法。这个方法用于获取类的特定公共构造函数。在这个例子中,我们获取了一个接受 `String` 类型参数的构造函数。
* `Constructor<Person>`: 这是获取到的构造函数的类型。它表明了这是一个可以构造 `Person` 对象的构造函数。

所以,这行代码的整体意思就是:从 `Person` 类中获取一个接受 `String` 类型参数的公共构造函数,并将其引用保存在名为 `constructor` 的变量中。

在获取到这个构造函数引用后,我们就可以使用 `constructor.newInstance("Alice")` 来创建新的 `Person` 对象了,这等同于调用 `new Person("Alice")`。这就是反射机制给我们带来的动态性。

为什么你会知道他需要接受一个string 类型的参数?

在这个调用中,String.class 是一个参数,它告诉 getConstructor 方法我们想要查找一个接受 String 类型参数的构造函数。这是 Java 反射 API 的一部分,它允许我们在运行时查询类、接口、字段和方法的信息。所以,我是通过这段代码推断出构造函数需要接受一个 String 类型参数的。

实际上,如果 Person 类没有这样一个构造函数,那么这行代码在运行时会抛出 NoSuchMethodException。因此,正确的构造函数的获取是基于我们在代码中提供的参数类型信息的。

那如果是查询其他类型的呢?

当然,如果要查询其他类型的构造函数或者方法,你可以根据参数类型来调整getConstructor或者getMethod的参数。下面举几个例子:

  1. 查询接受int类型参数的构造函数

如果Person类有一个接受int类型参数的构造函数,你可以这样获取:

Constructor<Person> constructor = personClass.getConstructor(int.class);
  1. 查询接受两个参数的构造函数

如果Person类有一个接受Stringint类型参数的构造函数,你可以这样获取:

Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
  1. 查询其他名称或参数的方法

如果你想要获取名为setAge并接受一个int参数的方法,你可以这样获取:

Method method = personClass.getMethod("setAge", int.class);

这些例子说明了如何基于参数类型来获取构造函数或者方法。核心的思想是使用对应的类型类(如String.classint.class等)作为参数传递给getConstructorgetMethod方法。这样,反射API就知道你要查询哪个具体的构造函数或方法了。

newInstance("Red"); 是什么意思

newInstance("Red")Constructor 类的一个方法,用于创建该构造函数所代表类的新实例。在这个例子中,newInstance("Red") 调用了 legoBlockClass 中接受的参数类型为 String 的构造函数,并将 "Red" 作为参数传递给了这个构造函数,然后创建了这个类的一个新实例。

简而言之,newInstance("Red") 就是在说:“我想创建一个新的 legoBlockClass 类型的对象,并且我想使用接受一个 String 参数(在这个例子中是 "Red")的构造函数来创建它”。

legoBlockClass 中接受的参数类型为 String 的构造函数 具体是哪个构造函数

legoBlockClass 中接受的参数类型为 String 的构造函数就是 legoBlockClass 类中定义的一个构造函数,这个构造函数的参数列表只有一个类型为 String 的参数。

例如,如果 legoBlockClass 是这样定义的:

public class LegoBlock {
    private String color;

    public LegoBlock(String color) {
        this.color = color;
    }

    // 其他方法...
}

在这个例子中,LegoBlock 类有一个接受 String 类型参数的构造函数,即 public LegoBlock(String color)。这就是 legoBlockClass 中接受的参数类型为 String 的构造函数。当我们调用 newInstance("Red") 时,实际上就是调用了这个构造函数,并传入了 "Red" 作为 color 参数的值。

如何判断是否为构造函数?

这是因为构造函数的特性和定义决定的。在面向对象编程中,构造函数是一个特殊类型的方法,它用于初始化一个新创建的对象。构造函数通常具有以下几个特点:

  1. 与类同名:构造函数的名称与类的名称完全相同。
  2. 无返回类型:与其他方法不同,构造函数没有返回类型,甚至连 void 也没有。
  3. 用于初始化对象:当创建类的新实例(对象)时,会自动调用构造函数。它通常用于设置初始状态或执行其他必要的初始化任务。

以你给出的 LegoBlock 类为例,LegoBlock(String color) 满足以上所有条件,因此它是一个构造函数。

相对地,普通的方法通常具有特定的返回类型和特定的功能,它们不是为了初始化对象,而是为了执行某些操作或计算。