1. Overview
1.概述
In this tutorial, we will explore Java reflection, which allows us to inspect and/or modify runtime attributes of classes, interfaces, fields and methods. This particularly comes in handy when we don’t know their names at compile time.
在本教程中,我们将探讨Java反射,它允许我们检查和/或修改类、接口、字段和方法的运行时属性。当我们在编译时不知道它们的名字时,这就特别方便了。
Additionally, we can instantiate new objects, invoke methods and get or set field values using reflection.
此外,我们可以使用反射来实例化新对象、调用方法以及获取或设置字段值。
2. Project Setup
2.项目设置
To use Java reflection, we don’t need to include any special jars, any special configuration or Maven dependencies. The JDK ships with a group of classes that are bundled in the java.lang.reflect package specifically for this purpose.
为了使用Java反射,我们不需要包含任何特殊的jars、任何特殊的配置或Maven依赖。JDK提供了一组类,它们被捆绑在java.lang.reflect包中,专门用于此目的。
So, all we need to do is to make the following import into our code:
因此,我们需要做的就是将以下内容导入我们的代码中。
import java.lang.reflect.*;
And we are good to go.
我们就可以出发了。
To get access to the class, method and field information of an instance, we call the getClass method, which returns the runtime class representation of the object. The returned class object provides methods for accessing information about a class.
为了获取一个实例的类、方法和字段信息,我们调用getClass方法,它返回对象的运行时类表示。返回的class对象提供了用于访问类信息的方法。
3. Simple Example
3.简单的例子
To get our feet wet, we’ll look at a very basic example that inspects the fields of a simple Java object at runtime.
为了让我们的脚踏实地,我们将看一个非常基本的例子,在运行时检查一个简单的Java对象的字段。
Let’s create a simple Person class with only name and age fields and no methods at all.
让我们创建一个简单的Person类,只有name和age字段,没有任何方法。
Here is the Person class:
这里是人物类。
public class Person {
private String name;
private int age;
}
We’ll now use Java reflection to discover the names of all fields of this class.
现在我们将使用Java反射来发现这个类的所有字段的名称。
To appreciate the power of reflection, let’s construct a Person object and use Object as the reference type:
为了体会反射的力量,让我们构造一个Person对象并使用Object作为引用类型。
@Test
public void givenObject_whenGetsFieldNamesAtRuntime_thenCorrect() {
Object person = new Person();
Field[] fields = person.getClass().getDeclaredFields();
List<String> actualFieldNames = getFieldNames(fields);
assertTrue(Arrays.asList("name", "age")
.containsAll(actualFieldNames));
}
This test shows us that we are able to get an array of Field objects from our person object, even if the reference to the object is a parent type of that object.
这个测试告诉我们,我们能够从我们的Field对象中得到一个F数组,即使该对象的引用是该对象的父类型。
In the above example, we were only interested in the names of those fields. But there is much more that can be done, and we will see examples of this in the next sections.
在上面的例子中,我们只对这些字段的名称感兴趣。但是还有很多事情可以做,我们将在接下来的章节中看到这方面的例子。
Notice how we use a helper method to extract the actual field names.
注意我们是如何使用一个辅助方法来提取实际的字段名。
It’s a very basic code:
这是一个非常基本的代码。
private static List<String> getFieldNames(Field[] fields) {
List<String> fieldNames = new ArrayList<>();
for (Field field : fields)
fieldNames.add(field.getName());
return fieldNames;
}
4. Java Reflection Use Cases
4.Java反射使用案例
Before we proceed to the different features of Java reflection, we’ll discuss some of the common uses we may find for it. Java reflection is extremely powerful and can come in very handy in a number of ways.
在我们继续讨论Java反射的不同特性之前,我们先讨论一下我们可能发现的一些常见用途。Java反射的功能非常强大,在很多方面都能派上用场。
For instance, in many cases, we have a naming convention for database tables. We may choose to add consistency by prefixing our table names with tbl_ so that a table with student data is called tbl_student_data.
例如,在许多情况下,我们有一个数据库表的命名惯例。我们可以选择在表名前加上tbl_来增加一致性,这样,一个包含学生数据的表就被称为tbl_student_data。
In such cases, we could name the Java object holding student data as Student or StudentData. Then using the CRUD paradigm, we have one entry point for each operation so that Create operations only receive an Object parameter.
在这种情况下,我们可以将持有学生数据的Java对象命名为Student或StudentData。然后使用CRUD范式,我们为每个操作设置一个入口点,这样Create操作就只能接收一个Object参数。
We then use reflection to retrieve the object name and field names. At this point, we can map this data to a DB table and assign the object field values to the appropriate DB field names.
然后我们使用反射来检索对象名称和字段名。在这一点上,我们可以将这些数据映射到DB表,并将对象的字段值分配给适当的DB字段名。
5. Inspecting Java Classes
5.检查Java类
In this section, we will explore the most fundamental component in the Java Reflection API. Java class objects, as we mentioned earlier, give us access to the internal details of any object.
在本节中,我们将探讨Java反射API中最基本的组件。正如我们前面提到的,Java类对象让我们可以访问任何对象的内部细节。
We are going to examine internal details such as an object’s class name, modifiers, fields, methods, implemented interfaces, etc.
我们将检查内部细节,如一个对象的类名、修改器、字段、方法、实现的接口等。
5.1. Getting Ready
5.1.准备就绪
To get a firm grip on the reflection API as applied to Java classes and have examples with variety, let’s create an abstract Animal class that implements the Eating interface. This interface defines the eating behavior of any concrete Animal object we create.
为了牢牢掌握应用于Java类的反射API,并有多种多样的例子,让我们创建一个抽象的Animal类,实现Eating接口。这个接口定义了我们创建的任何具体Animal对象的进食行为。
First, here is the Eating interface:
首先,这里是Eating界面。
public interface Eating {
String eats();
}
And here is the concrete Animal implementation of the Eating interface:
这里是Animal对Eating接口的具体实现。
public abstract class Animal implements Eating {
public static String CATEGORY = "domestic";
private String name;
protected abstract String getSound();
// constructor, standard getters and setters omitted
}
Let’s also create another interface called Locomotion that describes how an animal moves:
让我们再创建一个名为Locomotion的接口,描述动物如何移动。
public interface Locomotion {
String getLocomotion();
}
We’ll now create a concrete class called Goat that extends Animal and implements Locomotion.
我们现在要创建一个名为Goat的具体类,它扩展了Animal并实现了Locomotion。
Since the superclass implements Eating, Goat will have to implement that interface’s methods as well:
由于超类实现了Eating,Goat也将不得不实现该接口的方法。
public class Goat extends Animal implements Locomotion {
@Override
protected String getSound() {
return "bleat";
}
@Override
public String getLocomotion() {
return "walks";
}
@Override
public String eats() {
return "grass";
}
// constructor omitted
}
From this point onward, we will use Java reflection to inspect aspects of Java objects that appear in the classes and interfaces above.
从这一点开始,我们将使用Java反射来检查上述类和接口中出现的Java对象的各个方面。
5.2. Class Names
5.2.类名
Let’s start by getting the name of an object from the Class:
让我们先从Class中获取一个对象的名字。
@Test
public void givenObject_whenGetsClassName_thenCorrect() {
Object goat = new Goat("goat");
Class<?> clazz = goat.getClass();
assertEquals("Goat", clazz.getSimpleName());
assertEquals("com.baeldung.reflection.Goat", clazz.getName());
assertEquals("com.baeldung.reflection.Goat", clazz.getCanonicalName());
}
Note that the getSimpleName method of Class returns the basic name of the object as it would appear in its declaration. Then the other two methods return the fully qualified class name including the package declaration.
请注意,Class的getSimpleName方法返回对象的基本名称,因为它将出现在其声明中。然后,其他两个方法返回包括包声明在内的完全合格的类名。
Let’s also see how we can create an object of the Goat class if we only know its fully qualified class name:
我们也来看看,如果我们只知道Goat类的全称,我们如何能创建一个Goat类的对象。
@Test
public void givenClassName_whenCreatesObject_thenCorrect(){
Class<?> clazz = Class.forName("com.baeldung.reflection.Goat");
assertEquals("Goat", clazz.getSimpleName());
assertEquals("com.baeldung.reflection.Goat", clazz.getName());
assertEquals("com.baeldung.reflection.Goat", clazz.getCanonicalName());
}
Notice that the name we pass to the static forName method should include the package information. Otherwise, we will get a ClassNotFoundException.
注意,我们传递给静态forName方法的名字应该包括包的信息。否则,我们将得到一个ClassNotFoundException。
5.3. Class Modifiers
5.3.类修改器
We can determine the modifiers used in a class by calling the getModifiers method, which returns an Integer. Each modifier is a flag bit that is either set or cleared.
我们可以通过调用getModifiers方法来确定类中使用的修改器,该方法返回一个Integer。每个修饰符都是一个标志位,要么被设置,要么被清除。
The java.lang.reflect.Modifier class offers static methods that analyze the returned Integer for the presence or absence of a specific modifier.
java.lang.reflect.Modifier类提供了静态方法来分析返回的Integer是否存在一个特定的修饰符。
Let’s confirm the modifiers of some of the classes we defined above:
让我们确认一下我们上面定义的一些类的修饰语。
@Test
public void givenClass_whenRecognisesModifiers_thenCorrect() {
Class<?> goatClass = Class.forName("com.baeldung.reflection.Goat");
Class<?> animalClass = Class.forName("com.baeldung.reflection.Animal");
int goatMods = goatClass.getModifiers();
int animalMods = animalClass.getModifiers();
assertTrue(Modifier.isPublic(goatMods));
assertTrue(Modifier.isAbstract(animalMods));
assertTrue(Modifier.isPublic(animalMods));
}
We are able to inspect modifiers of any class located in a library jar that we are importing into our project.
我们能够检查位于我们正在导入项目的库jar中的任何类的修改器。
In most cases, we may need to use the forName approach rather than the full-blown instantiation since that would be an expensive process in the case of memory-heavy classes.
在大多数情况下,我们可能需要使用forName方法,而不是完全的实例化,因为对于内存大的类来说,这将是一个昂贵的过程。
5.4. Package Information
5.4.包装信息
By using Java reflection, we are also able to get information about the package of any class or object. This data is bundled inside the Package class, which is returned by a call to getPackage method on the class object.
通过使用Java反射,我们也能够获得关于任何类或对象的包的信息。这些数据被捆绑在Package类内,通过调用类对象的getPackage方法返回。
Let’s run a test to retrieve the package name:
让我们运行一个测试来检索包的名称。
@Test
public void givenClass_whenGetsPackageInfo_thenCorrect() {
Goat goat = new Goat("goat");
Class<?> goatClass = goat.getClass();
Package pkg = goatClass.getPackage();
assertEquals("com.baeldung.reflection", pkg.getName());
}
5.5. Superclass
5.5.超级类
We are also able to obtain the superclass of any Java class by using Java reflection.
我们也能够通过使用Java反射来获得任何Java类的超类。
In many cases, especially while using library classes or Java’s built-in classes, we may not know beforehand the superclass of an object we are using. This subsection will show how to obtain this information.
在许多情况下,特别是在使用库类或Java的内置类时,我们可能事先不知道我们正在使用的对象的超类。本小节将介绍如何获得这一信息。
Let’s go ahead and determine the superclass of Goat.
让我们继续下去,确定Goat的超类。
Additionally, we also show that java.lang.String class is a subclass of java.lang.Object class:
此外,我们还表明,java.lang.String类是java.lang.Object类的一个子类。
@Test
public void givenClass_whenGetsSuperClass_thenCorrect() {
Goat goat = new Goat("goat");
String str = "any string";
Class<?> goatClass = goat.getClass();
Class<?> goatSuperClass = goatClass.getSuperclass();
assertEquals("Animal", goatSuperClass.getSimpleName());
assertEquals("Object", str.getClass().getSuperclass().getSimpleName());
}
5.6. Implemented Interfaces
5.6.已实现的接口
Using Java reflection, we are also able to get the list of interfaces implemented by a given class.
使用Java反射,我们也能够获得一个给定的类所实现的接口列表。
Let’s retrieve the class types of the interfaces implemented by the Goat class and the Animal abstract class:
让我们检索一下Goat类和Animal抽象类所实现的接口的类别类型。
@Test
public void givenClass_whenGetsImplementedInterfaces_thenCorrect(){
Class<?> goatClass = Class.forName("com.baeldung.reflection.Goat");
Class<?> animalClass = Class.forName("com.baeldung.reflection.Animal");
Class<?>[] goatInterfaces = goatClass.getInterfaces();
Class<?>[] animalInterfaces = animalClass.getInterfaces();
assertEquals(1, goatInterfaces.length);
assertEquals(1, animalInterfaces.length);
assertEquals("Locomotion", goatInterfaces[0].getSimpleName());
assertEquals("Eating", animalInterfaces[0].getSimpleName());
}
Notice from the assertions that each class implements only a single interface. Inspecting the names of these interfaces, we find that Goat implements Locomotion and Animal implements Eating, just as it appears in our code.
从断言中注意到,每个类只实现了一个接口。检查这些接口的名称,我们发现Goat实现了Locomotion,Animal实现了Eating,就像我们的代码中出现的那样。
We can see that Goat is a subclass of the abstract class Animal and implements the interface method eats(). Then, Goat also implements the Eating interface.
我们可以看到,Goat是抽象类Animal的子类,并且实现了接口方法eats()。那么,Goat也实现了Eating接口。
It is therefore worth noting that only those interfaces that a class explicitly declares as implemented with the implements keyword appear in the returned array.
因此,值得注意的是,只有那些类明确声明用implements关键字实现的接口会出现在返回的数组中。
So, even if a class implements interface methods because its superclass implements that interface, but the subclass does not directly declare that interface with the implements keyword, that interface will not appear in the array of interfaces.
所以,即使一个类实现了接口方法,因为它的超类实现了该接口,但子类没有用implements关键字直接声明该接口,该接口也不会出现在接口阵列中。
5.7. Constructors, Methods and Fields
5.7.构造函数、方法和字段
With Java reflection, we are able to inspect the constructors of any object’s class as well as methods and fields.
通过Java反射,我们能够检查任何对象的类的构造函数,以及方法和字段。
Later, we’ll be able to see deeper inspections on each of these components of a class. But for now, it’s enough to just get their names and compare them with what we expect.
稍后,我们将能够看到对一个类的这些组成部分的更深入的检查。但现在,我们只需要得到它们的名字,并将它们与我们所期望的进行比较就足够了。
Let’s see how to get the constructor of the Goat class:
让我们看看如何获得Goat类的构造函数。
@Test
public void givenClass_whenGetsConstructor_thenCorrect(){
Class<?> goatClass = Class.forName("com.baeldung.reflection.Goat");
Constructor<?>[] constructors = goatClass.getConstructors();
assertEquals(1, constructors.length);
assertEquals("com.baeldung.reflection.Goat", constructors[0].getName());
}
We can also inspect the fields of the Animal class:
我们还可以检查Animal类的字段。
@Test
public void givenClass_whenGetsFields_thenCorrect(){
Class<?> animalClass = Class.forName("com.baeldung.reflection.Animal");
Field[] fields = animalClass.getDeclaredFields();
List<String> actualFields = getFieldNames(fields);
assertEquals(2, actualFields.size());
assertTrue(actualFields.containsAll(Arrays.asList("name", "CATEGORY")));
}
And we can similarly inspect the methods of the Animal class:
而我们也可以类似地检查Animal类的方法。
@Test
public void givenClass_whenGetsMethods_thenCorrect(){
Class<?> animalClass = Class.forName("com.baeldung.reflection.Animal");
Method[] methods = animalClass.getDeclaredMethods();
List<String> actualMethods = getMethodNames(methods);
assertEquals(4, actualMethods.size());
assertTrue(actualMethods.containsAll(Arrays.asList("getName",
"setName", "getSound")));
}
Just like getFieldNames, we have added a helper method to retrieve method names from an array of Method objects:
就像getFieldNames一样,我们添加了一个辅助方法,从Method对象的数组中检索方法名称。
private static List<String> getMethodNames(Method[] methods) {
List<String> methodNames = new ArrayList<>();
for (Method method : methods)
methodNames.add(method.getName());
return methodNames;
}
6. Inspecting Constructors
6.检查构造器
With Java reflection, we can inspect constructors of any class and even create class objects at runtime. This is made possible by the java.lang.reflect.Constructor class.
通过Java反射,我们可以检查任何类的构造器,甚至在运行时创建类对象。这是由java.lang.reflect.Constructor类实现的。
Earlier, we only looked at how to get the array of Constructor objects, from which we were able to get the names of the constructors.
早些时候,我们只看了如何获得构造函数对象的数组,我们能够从中获得构造函数的名称。
In this section, we will focus on how to retrieve specific constructors.
在本节中,我们将重点讨论如何检索特定的构造函数。
In Java, as we know, no two constructors of a class share exactly the same method signature. So, we will use this uniqueness to get one constructor from many.
在Java中,我们知道,一个类中没有两个构造函数有完全相同的方法签名。因此,我们将利用这种唯一性,从许多构造函数中得到一个构造函数。
To appreciate the features of this class, we will create a Bird subclass of Animal with three constructors.
为了体会这个类的特点,我们将创建一个Bird的子类Animal,有三个构造函数。
We will not implement Locomotion so that we can specify that behavior using a constructor argument, to add still more variety:
我们不会实现Locomotion,这样我们就可以使用构造函数参数来指定该行为,以增加更多的多样性。
public class Bird extends Animal {
private boolean walks;
public Bird() {
super("bird");
}
public Bird(String name, boolean walks) {
super(name);
setWalks(walks);
}
public Bird(String name) {
super(name);
}
public boolean walks() {
return walks;
}
// standard setters and overridden methods
}
Let’s confirm by using reflection that this class has three constructors:
让我们用反射来确认这个类有三个构造函数。
@Test
public void givenClass_whenGetsAllConstructors_thenCorrect() {
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Constructor<?>[] constructors = birdClass.getConstructors();
assertEquals(3, constructors.length);
}
Next, we will retrieve each constructor for the Bird class by passing the constructor’s parameter class types in declared order:
接下来,我们将通过按声明的顺序传递构造函数的参数类类型来检索Bird类的每个构造函数。
@Test
public void givenClass_whenGetsEachConstructorByParamTypes_thenCorrect(){
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Constructor<?> cons1 = birdClass.getConstructor();
Constructor<?> cons2 = birdClass.getConstructor(String.class);
Constructor<?> cons3 = birdClass.getConstructor(String.class, boolean.class);
}
There is no need for assertion since we’ll get a NoSuchMethodException and the test will automatically fail when a constructor with given parameter types in the given order does not exist.
不需要断言,因为我们会得到一个NoSuchMethodException ,当具有给定参数类型的构造函数不存在时,测试将自动失败。
In the last test, we’ll see how to instantiate objects at runtime while supplying their parameters:
在最后一个测试中,我们将看到如何在运行时对对象进行实例化,同时提供它们的参数。
@Test
public void givenClass_whenInstantiatesObjectsAtRuntime_thenCorrect() {
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Constructor<?> cons1 = birdClass.getConstructor();
Constructor<?> cons2 = birdClass.getConstructor(String.class);
Constructor<?> cons3 = birdClass.getConstructor(String.class,
boolean.class);
Bird bird1 = (Bird) cons1.newInstance();
Bird bird2 = (Bird) cons2.newInstance("Weaver bird");
Bird bird3 = (Bird) cons3.newInstance("dove", true);
assertEquals("bird", bird1.getName());
assertEquals("Weaver bird", bird2.getName());
assertEquals("dove", bird3.getName());
assertFalse(bird1.walks());
assertTrue(bird3.walks());
}
We instantiate class objects by calling the newInstance method of Constructor class and passing the required parameters in declared order. We then cast the result to the required type.
我们通过调用Constructor类的newInstance方法来实例化类对象,并按照声明的顺序传递所需的参数。然后,我们将结果转为所需的类型。
It’s also possible to call the default constructor using the Class.newInstance() method. However, this method has been deprecated since Java 9, and we shouldn’t use it in modern Java projects.
也可以使用Class.newInstance()方法来调用默认构造函数。然而,这个方法从Java 9开始就被废弃了,我们不应该在现代Java项目中使用它。
For bird1, we use the default constructor that automatically sets the name to bird from our Bird code, and we confirm that with a test.
对于bird1,我们使用默认的构造函数,从我们的Bird代码中自动设置名称为bird,我们通过测试来确认。
We then instantiate bird2 with only a name and test as well. Remember that when we don’t set locomotion behavior, it defaults to false as seen in the last two assertions.
然后我们实例化bird2,也只用一个名字和测试。请记住,当我们不设置运动行为时,它默认为false,正如在最后两个断言中看到的那样。
7. Inspecting Fields
7.检查领域
Previously, we only inspected the names of fields. In this section, we will show how to get and set their values at runtime.
之前,我们只检查了字段的名称。在本节中,我们将展示如何在运行时获取和设置其值。
There are two main methods used to inspect fields of a class at runtime: getFields() and getField(fieldName).
有两个主要的方法用于在运行时检查一个类的字段。getFields()和getField(fieldName)。
The getFields() method returns all accessible public fields of the class in question. It will return all the public fields in both the class and all superclasses.
getFields()方法返回相关类的所有可访问的公共字段。它将返回该类和所有超类中的所有公共字段。
For instance, when we call this method on the Bird class, we will only get the CATEGORY field of its superclass, Animal, since Bird itself does not declare any public fields:
例如,当我们在Bird类上调用这个方法时,我们将只得到其超类Animal的CATEGORY字段,因为Bird本身并没有声明任何公共字段。
@Test
public void givenClass_whenGetsPublicFields_thenCorrect() {
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Field[] fields = birdClass.getFields();
assertEquals(1, fields.length);
assertEquals("CATEGORY", fields[0].getName());
}
This method also has a variant called getField that returns only one Field object by taking the name of the field:
这个方法也有一个变体,叫做getField,通过获取字段的名称,只返回一个Field对象。
@Test
public void givenClass_whenGetsPublicFieldByName_thenCorrect() {
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Field field = birdClass.getField("CATEGORY");
assertEquals("CATEGORY", field.getName());
}
We are not able to access private fields declared in superclasses and not declared in the child class. This is why we can’t access the name field.
我们不能访问在超类中声明而在子类中没有声明的私有字段。这就是为什么我们不能访问name字段。
However, we can inspect private fields declared in the class we are dealing with by calling the getDeclaredFields method:
然而,我们可以通过调用getDeclaredFields方法来检查我们正在处理的类中声明的私有字段。
@Test
public void givenClass_whenGetsDeclaredFields_thenCorrect(){
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Field[] fields = birdClass.getDeclaredFields();
assertEquals(1, fields.length);
assertEquals("walks", fields[0].getName());
}
We can also use its other variant in case we know the name of the field:
在我们知道字段名称的情况下,我们也可以使用它的其他变体。
@Test
public void givenClass_whenGetsFieldsByName_thenCorrect() {
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Field field = birdClass.getDeclaredField("walks");
assertEquals("walks", field.getName());
}
If we get the name of the field wrong or type in an nonexistent field, we’ll get a NoSuchFieldException.
如果我们弄错了字段的名称或者输入了一个不存在的字段,我们会得到一个NoSuchFieldException。
Now we’ll get the field type:
现在我们将得到字段类型。
@Test
public void givenClassField_whenGetsType_thenCorrect() {
Field field = Class.forName("com.baeldung.reflection.Bird")
.getDeclaredField("walks");
Class<?> fieldClass = field.getType();
assertEquals("boolean", fieldClass.getSimpleName());
}
Next, let’s look at how to access field values and modify them.
接下来,让我们看看如何访问字段值并修改它们。
To get the value of a field, let alone set it, we have to first set it’s accessible by calling setAccessible method on the Field object and pass boolean true to it:
要获得一个字段的值,更不用说设置它了,我们必须首先通过调用Field对象上的setAccessible方法来设置它的可访问性,并向它传递布尔值true。
@Test
public void givenClassField_whenSetsAndGetsValue_thenCorrect() {
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Bird bird = (Bird) birdClass.getConstructor().newInstance();
Field field = birdClass.getDeclaredField("walks");
field.setAccessible(true);
assertFalse(field.getBoolean(bird));
assertFalse(bird.walks());
field.set(bird, true);
assertTrue(field.getBoolean(bird));
assertTrue(bird.walks());
}
In the above test, we ascertain that indeed the value of the walks field is false before setting it to true.
在上面的测试中,我们确定walks字段的值确实是假的,然后将其设置为真。
Notice how we use the Field object to set and get values by passing it the instance of the class we are dealing with and possibly the new value we want the field to have in that object.
请注意我们是如何使用Field对象来设置和获取值的,我们把我们正在处理的类的实例和可能的我们希望该对象中的字段的新值传递给它。
One important thing to note about Field objects is that when it is declared as public static, we don’t need an instance of the class containing them.
关于Field对象需要注意的一件事是,当它被声明为public static时,我们不需要包含它们的类的实例。
We can just pass null in its place and still obtain the default value of the field:
我们可以直接传递null来代替它,仍然可以获得该字段的默认值。
@Test
public void givenClassField_whenGetsAndSetsWithNull_thenCorrect(){
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Field field = birdClass.getField("CATEGORY");
field.setAccessible(true);
assertEquals("domestic", field.get(null));
}
8. Inspecting Methods
8.检查方法
In a previous example, we used reflection only to inspect method names. However, Java reflection is more powerful than that.
在前面的例子中,我们只用反射来检查方法名。然而,Java反射的功能比这更强大。
With Java reflection, we can invoke methods at runtime and pass them their required parameters, just like we did for constructors. Similarly, we can also invoke overloaded methods by specifying parameter types of each.
通过Java反射,我们可以在运行时调用方法,并将其所需的参数传递给他们,就像我们对构造函数所做的那样。同样地,我们也可以通过指定每个方法的参数类型来调用重载方法。
Just like fields, there are two main methods that we use for retrieving class methods. The getMethods method returns an array of all public methods of the class and superclasses.
就像字段一样,有两个主要的方法,我们用来检索类的方法。getMethods方法返回该类和超类的所有公共方法的数组。
This means that with this method, we can get public methods of the java.lang.Object class such as toString, hashCode and notifyAll:
这意味着通过这个方法,我们可以获得java.lang.Object类的公共方法,如toString、hashCode和notifyAll。
@Test
public void givenClass_whenGetsAllPublicMethods_thenCorrect(){
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Method[] methods = birdClass.getMethods();
List<String> methodNames = getMethodNames(methods);
assertTrue(methodNames.containsAll(Arrays
.asList("equals", "notifyAll", "hashCode",
"walks", "eats", "toString")));
}
To get only public methods of the class we are interested in, we have to use getDeclaredMethods method:
为了只获得我们感兴趣的类的公共方法,我们必须使用getDeclaredMethods方法。
@Test
public void givenClass_whenGetsOnlyDeclaredMethods_thenCorrect(){
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
List<String> actualMethodNames
= getMethodNames(birdClass.getDeclaredMethods());
List<String> expectedMethodNames = Arrays
.asList("setWalks", "walks", "getSound", "eats");
assertEquals(expectedMethodNames.size(), actualMethodNames.size());
assertTrue(expectedMethodNames.containsAll(actualMethodNames));
assertTrue(actualMethodNames.containsAll(expectedMethodNames));
}
Each of these methods has the singular variation that returns a single Method object whose name we know:
这些方法中的每一个都有单一的变化,即返回一个我们知道其名称的Method对象。
@Test
public void givenMethodName_whenGetsMethod_thenCorrect() throws Exception {
Bird bird = new Bird();
Method walksMethod = bird.getClass().getDeclaredMethod("walks");
Method setWalksMethod = bird.getClass().getDeclaredMethod("setWalks", boolean.class);
assertTrue(walksMethod.canAccess(bird));
assertTrue(setWalksMethod.canAccess(bird));
}
Notice how we retrieve individual methods and specify what parameter types they take. Those that don’t take parameter types are retrieved with an empty variable argument, leaving us with only a single argument, the method name.
注意我们是如何检索单个方法并指定它们的参数类型的。那些不接受参数类型的方法会被检索到一个空的变量参数,我们只剩下一个参数,即方法名称。
Next, we will show how to invoke a method at runtime.
接下来,我们将展示如何在运行时调用一个方法。
We know by default that the walks attribute of the Bird class is false.
我们知道默认情况下,Bird类的walks属性是false。
We want to call its setWalks method and set it to true:
我们要调用它的setWalks方法并将其设置为true。
@Test
public void givenMethod_whenInvokes_thenCorrect() {
Class<?> birdClass = Class.forName("com.baeldung.reflection.Bird");
Bird bird = (Bird) birdClass.getConstructor().newInstance();
Method setWalksMethod = birdClass.getDeclaredMethod("setWalks", boolean.class);
Method walksMethod = birdClass.getDeclaredMethod("walks");
boolean walks = (boolean) walksMethod.invoke(bird);
assertFalse(walks);
assertFalse(bird.walks());
setWalksMethod.invoke(bird, true);
boolean walks2 = (boolean) walksMethod.invoke(bird);
assertTrue(walks2);
assertTrue(bird.walks());
}
Notice how we first invoke the walks method and cast the return type to the appropriate data type and then check its value. We later invoke the setWalks method to change that value and test again.
请注意,我们首先调用walks方法,并将返回类型转换为适当的数据类型,然后检查其值。之后我们调用setWalks方法来改变该值,并再次测试。
9. Conclusion
9.结论
In this article, we covered the Java Reflection API and looked at how to use it to inspect classes, interfaces, fields and methods at runtime without prior knowledge of their internals by compile time.
在这篇文章中,我们介绍了Java Reflection API,并研究了如何在运行时使用它来检查类、接口、字段和方法,而不需要在编译时事先了解其内部情况。
The full source code and examples for this article can be found over on GitHub.
本文的完整源代码和示例可以在GitHub上找到over。