Java Reflection
Reflection is a powerful advanced feature provided by Java that allows a running Java program to examine or "reflect upon" itself, and manipulate classes, interfaces, fields, and methods at runtime.
What is Reflection?
The reflection mechanism allows a program at runtime to:
- Get the complete structure of any class (including its parent class, interfaces, constructors, methods, fields, etc.).
- Create instances of any class at runtime.
- Invoke methods of any object at runtime.
- Get or set field values of any object at runtime, even
privatemembers.
The core of reflection is the java.lang.Class class and a series of classes in the java.lang.reflect package, such as Constructor, Method, Field, etc.
The Class Object
For every class loaded into the JVM, a corresponding Class object is created in memory. This Class object contains all information about that class and is the entry point for reflection.
Three Ways to Get a Class Object
Using
.classon the class name: The most direct and safest way, checked at compile time.javaClass<String> stringClass = String.class;Using the
getClass()method on an object: If you already have an object instance, you can call itsgetClass()method.javaString s = "Hello"; Class<?> sClass = s.getClass();Using the fully qualified name with
Class.forName(): This is the most flexible way, allowing dynamic class loading at runtime based on a string.javatry { Class<?> driverClass = Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); }
Using Reflection to Inspect Class Information
Once you have a Class object, you can use it to explore the internal structure of the class.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionInfoExample {
public static void main(String[] args) {
Class<String> clazz = String.class;
// Get class name
System.out.println("Class name: " + clazz.getName());
System.out.println("Simple class name: " + clazz.getSimpleName());
// Get all public fields
System.out.println("\nPublic Fields:");
for (Field field : clazz.getFields()) {
System.out.println(" - " + field.getName());
}
// Get all declared fields (including private)
System.out.println("\nDeclared Fields:");
for (Field field : clazz.getDeclaredFields()) {
System.out.println(" - " + field.getName());
}
// Get all public methods
System.out.println("\nPublic Methods:");
for (Method method : clazz.getMethods()) {
System.out.println(" - " + method.getName());
}
}
}Using Reflection to Create Instances and Invoke Methods
The most powerful feature of reflection is dynamically creating objects and executing operations at runtime.
Creating Instances
- If the class has a public no-argument constructor, you can directly call
clazz.newInstance()(deprecated) orclazz.getDeclaredConstructor().newInstance(). - If you need to use a parameterized constructor, you first need to get the
Constructorobject.
Invoking Methods
- Get the
Methodobject usinggetMethod()orgetDeclaredMethod(). - Call
method.invoke(object, args...)to execute the method.
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectionActionExample {
public static void main(String[] args) throws Exception {
// 1. Get Class object
Class<?> clazz = String.class;
// 2. Create instance
// Get String(char[]) constructor
Constructor<?> constructor = clazz.getConstructor(char[].class);
char[] value = {'H', 'e', 'l', 'l', 'o'};
Object instance = constructor.newInstance(value);
System.out.println("Created instance: " + instance); // "Hello"
// 3. Invoke method
// Get toUpperCase() method
Method method = clazz.getMethod("toUpperCase");
// Invoke method, for instance methods, the first parameter is the instance object
Object result = method.invoke(instance);
System.out.println("toUpperCase() result: " + result); // "HELLO"
}
}Accessing Private Members
Reflection can even bypass private access restrictions. This is useful in unit testing or certain frameworks, but should be used cautiously as it breaks encapsulation.
- Use
getDeclaredField()orgetDeclaredMethod()to get private members. - Before accessing, you must call
setAccessible(true)to disable access security checks.
// Assume there's a Person class
// public class Person {
// private String name;
// public Person(String name) { this.name = name; }
// }
// Person person = new Person("Alice");
// Field nameField = person.getClass().getDeclaredField("name");
// nameField.setAccessible(true); // Disable access checking
// String nameValue = (String) nameField.get(person);
// System.out.println(nameValue); // Output "Alice"Pros and Cons of Reflection
Pros:
- Flexibility and dynamism: Enables programs to load, inspect, and use classes unknown at compile time at runtime.
- Framework development: Is the cornerstone of many modern frameworks (such as Spring, Hibernate, JUnit) for implementing dependency injection, ORM, and other features.
Cons:
- Performance overhead: Reflection operations are much slower than direct code calls.
- Security issues: Can bypass access control and break encapsulation.
- Poor code readability: Reflection code is typically more complex and harder to understand and debug.