Reflection in Java is considered a key feature of dynamic languages. The Reflection mechanism allows a program to obtain internal information of any class during execution via the Reflection API, and directly manipulate the internal properties and methods of any object. Reflection is a powerful yet complex mechanism. The primary users of Reflection are tool developers, rather than application developers. If you're only interested in designing applications and not in building tools, there is no need to delve into it.
1. Features Provided by Reflection Mechanism (When to Use Reflection)
Determine the class of any object at runtime.
Create instances of any class at runtime.
Determine the member variables and methods of any class at runtime.
Invoke any object’s member variables and methods at runtime.
Generate dynamic proxies.
2. The Class
Class
During the program's runtime, the Java runtime system maintains a runtime type identifier for every object. This information tracks the class of each object. The virtual machine uses this runtime type information to select the appropriate method for execution. The class that stores this information is called Class
. The getClass()
method from the Object
class returns an instance of the Class
type. Below are the details:
public class ReflecTest { public static void main(String[] args) throws Exception { // Class is generic /* If a Person object represents a specific person, then a Class object will represent the properties of a specific class. */ // There are three ways to get a Class, the first one is as follows: Class<Person> clazz = Person.class; // Create an object of the runtime class Person corresponding to clazz // Creating an object of the class: call the newInstance() method of the Class object: the class must have a no-argument constructor and the constructor must have sufficient access privileges. Person person = clazz.newInstance(); // Alternatively, you can first create an object and use getClass() to get the Class object, from which you can deduce the structure of the target object Person personTest = new Person(); // The second way to get a Class is as follows: Class<? extends Person> personTestClass = personTest.getClass(); String clazzName = personTestClass.getName(); //com.reflection.learn.Person System.out.printf(clazzName); // The third way to get a Class is as follows: through the static method of Class Class<?> className = Class.forName("com.reflection.learn.Person"); // Use reflection to access specific attributes of the runtime class Field name = clazz.getField("name"); // To set the attribute, you need to pass the **object** and the value // Set the public attribute name.set(person, "Li Si"); // Access a private attribute Field age = clazz.getDeclaredField("age"); age.setAccessible(true); // Allow access to private field age.set(person, 20); // Use reflection to invoke a method (no parameters) Method display = clazz.getMethod("display"); // Execute this method, passing the object display.invoke(person); // If it's a static method, the invocation rule is as follows: // display.invoke(Person.class); // Call a method with parameters: public void getObject(String name) Method getObject = clazz.getMethod("getObject", String.class); // The method execution requires passing the object and parameter getObject.invoke(person, "Li Si"); } }
Explanation of java.lang.Class
:
This class is the source of reflection: we create a class, and through the compiler (javac.exe
), a corresponding .class
file is generated. Then, by using java.exe
(the JVM class loader), this .class
file is loaded into memory, where it becomes a runtime class stored in the buffer. This runtime class itself is an instance of Class
. A runtime class is loaded only once.
3. Invoking Specific Methods and Properties via Reflection (Important)
@Test public void test2() throws Exception { // Get a specified constructor Class<Person> clazz = Person.class; // Get a specified constructor Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class); Person person = declaredConstructor.newInstance("li", 20); // Output: Person{name='li', age=20} System.out.println(person.toString()); // Get a specified method Method getObject = clazz.getDeclaredMethod("getObject", String.class); getObject.invoke(person, "zhangsan"); }
4. Using Reflection to Analyze Class Capabilities
In the java.lang.reflect
package, there are three classes: Field
(fields), Method
(methods), and Constructor
(constructors). Each of these classes has a method called getName()
, which returns the name of the item. The Field
class has a getType()
method, which returns a Class
object that describes the type of the field. Both the Method
and Constructor
classes have methods like getParameterTypes()
to report their parameters. The Method
class also has a getReturnType()
method to report the return type.
These three classes also have a getModifiers()
method, which returns an integer value. This integer describes the access modifiers (such as public
, private
, etc.) using bit flags. You can convert these integers into readable access modifiers using the Modifier
class's static methods. The Class
class provides methods like getDeclaredFields()
, getDeclaredMethods()
, and getDeclaredConstructors()
, which return all the fields, methods, and constructors declared in the class, including private and protected members (but excluding inherited members).
A challenge with the get()
method arises when a field is a String
—it’s no problem treating it as an Object
. However, for fields like salary
, which is of type double
, the primitive data types are not objects. In such cases, you can use the Field
class's getDouble()
method, or call get()
, and the reflection mechanism will automatically box the value into the appropriate wrapper type (in this case, Double
).