Reflection
Reflection is the ability of a running Java program to inspect and manipulate its own classes, methods, fields, and constructors — at runtime, without knowing their names at compile time. The API lives in the java.lang.reflect package, and it is how a program can answer questions like “what methods does this object have?” or “can I create an instance of this class whose name I only just read from a config file?”
Ordinary code binds everything at compile time: you write new Car() and call car.drive() because you know, while writing the code, that Car exists and has a drive() method. Reflection flips this around — it lets code discover and invoke members whose identity is only known as data (a string, a file, an annotation) at runtime.
Getting a Class object
Every object in Java carries metadata about its own type, accessible through java.lang.Class. There are three common ways to obtain a Class reference.
Three ways to get a Class object
// 1. From an existing instance
String text = "hello";
Class<?> c1 = text.getClass();
// 2. From a type literal (compile-time known type)
Class<?> c2 = String.class;
// 3. From a fully-qualified class name (only known at runtime!)
Class<?> c3 = Class.forName("java.lang.String");
System.out.println(c1 == c2); // true — the JVM caches one Class per type
System.out.println(c3.getName()); // java.lang.StringClass.forName(String) is the classic entry point for “stringly-typed” class loading — for example, loading a JDBC driver by name, or instantiating a plugin class whose name came from a configuration file.
Inspecting a class
Once you have a Class object, you can list its constructors, methods, and fields — including ones marked private, if you explicitly ask reflection to bypass access checks.
Listing members of a class
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;
public class Inspector {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("java.util.ArrayList");
for (Constructor<?> ctor : clazz.getConstructors()) {
System.out.println("Constructor: " + ctor);
}
for (Method m : clazz.getDeclaredMethods()) {
System.out.println("Method: " + m.getName());
}
for (Field f : clazz.getDeclaredFields()) {
System.out.println("Field: " + f.getName());
}
}
}Invoking a method reflectively
Once you have a Method object, invoke() calls it on a target instance, passing arguments as an Object[] — the compiler cannot check argument types for you, so mistakes surface as runtime exceptions (InvocationTargetException, IllegalArgumentException) instead of compile errors.
Creating an instance and invoking a method by name
import java.lang.reflect.Method;
public class Greeter {
private String prefix;
public Greeter(String prefix) {
this.prefix = prefix;
}
public String greet(String name) {
return prefix + ", " + name + "!";
}
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("Greeter");
// Create an instance via the reflective constructor
Object greeter = clazz.getConstructor(String.class)
.newInstance("Hello");
// Look up and invoke the "greet" method by name
Method greetMethod = clazz.getMethod("greet", String.class);
Object result = greetMethod.invoke(greeter, "World");
System.out.println(result); // Hello, World!
}
}Accessing private members
Reflection can also read and write private fields and call private methods by disabling Java’s normal access checks with setAccessible(true). This is how many testing and serialization libraries peek inside objects without requiring public getters.
Reading a private field
import java.lang.reflect.Field;
Field prefixField = Greeter.class.getDeclaredField("prefix");
prefixField.setAccessible(true); // bypass the private modifier
String value = (String) prefixField.get(greeter);
System.out.println(value); // HelloReflection API | Purpose |
|---|---|
| Load a class by fully-qualified name at runtime |
| Get the runtime Class of an existing instance |
| List a class's fields (declared-only vs. including inherited public ones) |
| List a class's methods |
| Look up a constructor by parameter types |
| Call a method reflectively |
| Bypass access checks to read/write private members |
Reflection inspects and manipulates classes, methods, and fields at runtime, via
java.lang.reflect.Class.forName(),.getClass(), andType.classare the three ways to obtain aClassobject.Method.invoke()calls a method whose name was only known at runtime, trading compile-time safety for flexibility.Reflection is slower and bypasses encapsulation — use it deliberately, and prefer normal APIs when they suffice.
Spring, JUnit, and Jackson are all built on reflection, which is why understanding it clarifies how those frameworks work under the hood.