Reflection API
Inspect and manipulate classes, fields, and methods at runtime using the Java Reflection API.
Overview
Key Points
- •Class.forName("com.example.Foo") loads and returns the Class object at runtime
- •clazz.getDeclaredFields() returns all fields (including private); getFields() returns only public fields
- •field.setAccessible(true) bypasses access control for that field
- •clazz.getDeclaredMethod("name", paramTypes) finds a method by name and parameter types
- •method.invoke(instance, args) calls the method reflectively
- •clazz.getDeclaredConstructors() lists all constructors
- •constructor.newInstance(args) creates an instance without calling new
- •Reflection is slower than direct calls due to access checking and boxing; avoid in tight loops
- •Use reflection for framework code, not regular application logic
Examples
Inspecting class structure at runtime
import java.lang.reflect.*;
public class Main {
static class Person {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
public String greet() {
return "Hi, I'm " + name;
}
}
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;
System.out.println("Class: " + clazz.getName());
System.out.println("Fields:");
for (Field f : clazz.getDeclaredFields()) {
System.out.println(" " + f.getType().getSimpleName() + " " + f.getName());
}
System.out.println("Methods:");
for (Method m : clazz.getDeclaredMethods()) {
System.out.println(" " + m.getReturnType().getSimpleName() + " " + m.getName());
}
}
}getDeclaredFields() and getDeclaredMethods() include private members of the class itself but not inherited members. Use getFields() and getMethods() for public members including inherited ones.
Reading and writing private fields
import java.lang.reflect.Field;
public class Main {
static class Config {
private String apiKey = "secret-key-123";
private int maxConnections = 10;
}
public static void main(String[] args) throws Exception {
Config config = new Config();
Field apiKeyField = Config.class.getDeclaredField("apiKey");
apiKeyField.setAccessible(true); // bypass private access
// Read
System.out.println("API Key: " + apiKeyField.get(config));
// Write
apiKeyField.set(config, "new-key-456");
System.out.println("New Key: " + apiKeyField.get(config));
// Read an int field
Field connField = Config.class.getDeclaredField("maxConnections");
connField.setAccessible(true);
System.out.println("Max connections: " + connField.getInt(config));
}
}setAccessible(true) disables the normal access check. In Java 9+, this may be restricted by module system for JDK-internal classes, but your own application classes remain accessible.
Invoking a method by name at runtime
import java.lang.reflect.Method;
public class Main {
static class Calculator {
public int add(int a, int b) { return a + b; }
public int multiply(int a, int b) { return a * b; }
}
public static void main(String[] args) throws Exception {
Calculator calc = new Calculator();
String methodName = "add"; // could come from config/input
Method method = Calculator.class.getMethod(methodName, int.class, int.class);
Object result = method.invoke(calc, 6, 7);
System.out.println(methodName + "(6, 7) = " + result); // 13
// Dynamic dispatch: choose method at runtime
for (String name : new String[]{"add", "multiply"}) {
Method m = Calculator.class.getMethod(name, int.class, int.class);
System.out.println(name + ": " + m.invoke(calc, 3, 4));
}
}
}getMethod(name, paramTypes) finds public methods including inherited ones. getDeclaredMethod finds all methods of the class itself. The result of invoke is Object; cast it to the expected type.
Common Mistakes
- Forgetting to call setAccessible(true) on private fields/methods. Without it, accessing private members throws IllegalAccessException.
- Passing wrong parameter types to getDeclaredMethod. The parameter types must exactly match the method signature, including wrapper vs primitive (use int.class, not Integer.class for int parameters).
- Using reflection in tight inner loops. Reflection carries overhead from access checks and boxing. Cache the Method or Field object and call invoke repeatedly rather than looking up the method each iteration.
- Not handling InvocationTargetException. When a reflected method throws, the exception is wrapped in InvocationTargetException. Unwrap it with e.getCause() to get the original exception.
Note
Cache Method and Field objects at startup rather than looking them up on every call. Lookup involves string matching and access checking, but invoke on a cached Method is much cheaper. Many frameworks (Spring, Hibernate) do exactly this during initialization.
Exercises
Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.
Completed: 0 / 2 exercises
Knowledge Check
Question 1 of 4