Reflection
Reflection lets a running Java program inspect and manipulate classes, fields, methods, and constructors it did not know about at compile time — powerful for frameworks, costly and risky when overused.
Reflection is the ability of a Java program to examine and manipulate its own structure at run time: discovering a class's fields, methods, and constructors, reading or setting field values, and invoking methods or constructors dynamically, all through the java.lang.reflect package and java.lang.Class. It is the mechanism underlying most frameworks (dependency injection, ORMs, serialization libraries, testing frameworks) that need to operate generically on classes they've never seen at compile time — but it trades away compile-time safety and performance, so it should be reached for deliberately, not by default.
The Class Object: Entry Point Into Reflection
Every loaded class and interface in Java has a corresponding, unique instance of java.lang.Class at run time, which describes that type's structure and is the entry point for all reflective operations on it. Class objects are created and managed by the JVM's class-loading mechanism, not by application code with new.
Obtaining a Class Object
There are three common ways to get a Class instance: the .class literal (compile-time known type), calling getClass() on an existing instance (run-time actual type), or Class.forName(String) (dynamically, by fully-qualified name, throwing checked ClassNotFoundException if not found). Class.forName() is what makes fully dynamic, plugin-style loading of classes unknown at compile time possible.
Class<String> c1 = String.class; // compile-time literal
String s = "hello";
Class<?> c2 = s.getClass(); // run-time actual type
try {
Class<?> c3 = Class.forName("java.util.ArrayList"); // dynamic, by name
System.out.println(c3.getSimpleName()); // ArrayList
} catch (ClassNotFoundException e) {
// handle missing class
}Inspecting Fields
getFields() returns all public fields, including inherited ones; getDeclaredFields() returns all fields declared directly in that class (any access modifier) but NOT inherited ones. Each Field object exposes its name, type, modifiers, and get(Object)/set(Object, Object) methods to read/write its value on a given instance.
getDeclaredFields() sees both x and y regardless of visibility, but reading a private field's value still requires setAccessible(true) to bypass the normal access check.
import java.lang.reflect.Field;
class Point {
public int x;
private int y;
Point(int x, int y) { this.x = x; this.y = y; }
}
public class FieldInspect {
public static void main(String[] args) throws Exception {
Point p = new Point(3, 4);
Class<?> clazz = p.getClass();
for (Field f : clazz.getDeclaredFields()) {
f.setAccessible(true); // needed to read the private field y
System.out.println(f.getName() + " = " + f.get(p));
}
}
}Inspecting and Invoking Methods
getMethods() returns all public methods (including inherited ones, e.g. from Object); getDeclaredMethods() returns methods declared directly on the class regardless of access modifier, excluding inherited ones. Method.invoke(Object target, Object... args) calls the method reflectively, wrapping any exception thrown by the underlying method in an InvocationTargetException (whose getCause() gives the real exception).
import java.lang.reflect.Method;
public class MethodInvokeDemo {
public static void main(String[] args) throws Exception {
String text = "hello world";
Method m = String.class.getMethod("toUpperCase");
Object result = m.invoke(text); // equivalent to text.toUpperCase()
System.out.println(result); // HELLO WORLD
Method substring = String.class.getMethod("substring", int.class, int.class);
System.out.println(substring.invoke(text, 0, 5)); // hello
}
}Inspecting and Invoking Constructors
getConstructors() returns public constructors; getDeclaredConstructors() returns all constructors regardless of access modifier. Constructor.newInstance(Object... args) creates a new instance reflectively, the mechanism frameworks use to instantiate classes whose concrete type is only known at run time (e.g. from a configuration file or annotation).
import java.lang.reflect.Constructor;
class Widget {
private final String name;
public Widget(String name) { this.name = name; }
@Override public String toString() { return "Widget[" + name + "]"; }
}
public class ConstructorDemo {
public static void main(String[] args) throws Exception {
Constructor<Widget> ctor = Widget.class.getConstructor(String.class);
Widget w = ctor.newInstance("Button");
System.out.println(w); // Widget[Button]
}
}Accessibility: Bypassing Access Control With setAccessible()
Reflection's default behavior still honors normal Java access control (private/protected/package-private members throw IllegalAccessException on get()/invoke()/newInstance() otherwise). AccessibleObject.setAccessible(true) — available on Field, Method, and Constructor — suppresses that check, letting reflective code touch private state directly. This capability is exactly why reflection is such a powerful tool for frameworks (and testing) and exactly why it's a security/encapsulation concern: it lets code deliberately break the encapsulation the class author intended.
Since Java's module system (JPMS, Java 9+), setAccessible() can additionally be restricted or denied by module boundaries (a module must 'open' a package for deep reflection to be permitted across module boundaries), adding another layer of control over how far reflection can reach.
Reflection Costs: Performance Overhead
Reflective operations are meaningfully slower than their direct equivalents: type checks and access checks that the compiler would normally do statically must instead happen dynamically on every reflective call (unless bypassed), method/field lookup happens by name rather than through a compiled reference, and JIT optimization is typically less effective on reflective call paths than on direct calls. Modern JVMs have narrowed this gap substantially with caching and bytecode-generation tricks internally, but reflective code is still generally slower than equivalent direct code, and this cost compounds when reflection runs in a hot loop rather than during one-time setup.
A common mitigation pattern used by frameworks is to do reflective lookups once (e.g. at startup or first use, caching the resulting Method/Field/Constructor objects) rather than repeating the lookup on every call.
Use Cases: Where Reflection Earns Its Keep
Reflection is the foundation of many widely used tools: dependency injection frameworks (Spring, etc.) that instantiate and wire objects based on annotations/configuration; ORMs (Hibernate/JPA) that map database rows to object fields generically; JSON/serialization libraries (Jackson, Gson) that read/write arbitrary object graphs without hand-written per-class code; testing frameworks (JUnit) that discover and invoke annotated test methods; and general-purpose tools like debuggers and IDEs that need to inspect arbitrary objects at run time.
Limitations and Risks
Reflection trades compile-time type safety for run-time flexibility: a typo in a field/method name passed as a String is not caught until the code actually runs (as NoSuchFieldException/NoSuchMethodException), unlike a normal compile-time reference. Overusing reflection where a direct call would work makes code harder to read, harder for IDEs to navigate/refactor safely (renaming a method doesn't automatically update string-based reflective lookups of it), and, as noted, generally slower. setAccessible(true) also deliberately breaks encapsulation, which can violate a class's intended invariants if misused, and is increasingly constrained by the module system in modern Java.
Practical Example: A Minimal Generic Object Inspector
This is a simplified sketch of the kind of generic field-walking logic serialization and ORM libraries build on: no code here knows about Point specifically, yet it can inspect and print any object's fields.
import java.lang.reflect.Field;
public class ObjectDumper {
static void dump(Object obj) throws IllegalAccessException {
Class<?> clazz = obj.getClass();
System.out.println(clazz.getSimpleName() + " {");
for (Field f : clazz.getDeclaredFields()) {
f.setAccessible(true);
System.out.println(" " + f.getName() + " = " + f.get(obj));
}
System.out.println("}");
}
public static void main(String[] args) throws IllegalAccessException {
dump(new Point(3, 4));
}
}
class Point {
private int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
}Mental Model
Think of a normal Java program as reading a class's structure the way you'd read a fixed, pre-printed instruction manual — resolved once at compile time. Reflection is instead asking the running program 'show me your own instruction manual, right now, so I can look up a page by name and follow it' — powerful and flexible, but every lookup costs a bit more than reading a page you already knew the number of.
Key Takeaways
Every type has a runtime Class object, obtainable via .class, getClass(), or the dynamic Class.forName().
Field, Method, and Constructor objects let code inspect and invoke a class's members by name at run time, discovered via getDeclaredX()/getX() family methods.
setAccessible(true) bypasses normal Java access control for reflective access, and is itself constrained further by the module system since Java 9.
Reflective operations are slower than direct calls due to dynamic lookup and reduced JIT optimization; cache Field/Method/Constructor lookups rather than repeating them in hot paths.
Reflection underlies most frameworks that operate generically on unknown classes (DI, ORM, serialization, testing) but trades away compile-time safety and refactoring support.
Reach for reflection deliberately, for genuine framework-style generic needs — not as a substitute for a straightforward direct call or interface.
Field f = obj.getClass().getDeclaredField("privateField"); f.get(obj); // throws IllegalAccessException
Field f = obj.getClass().getDeclaredField("privateField"); f.setAccessible(true); f.get(obj);
Reflective access to a private (or otherwise inaccessible) member still respects normal access control unless setAccessible(true) is called first.
for (Item item : items) { Method m = item.getClass().getMethod("process"); // repeated lookup every iteration m.invoke(item); }
Method m = Item.class.getMethod("process"); // looked up once for (Item item : items) { m.invoke(item); }
Repeating a reflective lookup inside a hot loop pays its overhead on every iteration; caching the Method/Field/Constructor object once and reusing it avoids redundant lookup cost.
clazz.getMethod("proccess"); // typo not caught until runtime NoSuchMethodException
// Prefer a direct call or interface reference wherever the target type/method IS known at compile time; // reserve reflection for genuinely dynamic/unknown-at-compile-time scenarios.
Reflective lookups by String name lose compile-time checking entirely; a typo or renamed method is only discovered at run time, unlike a normal method reference the compiler verifies.
Method m = obj.getClass().getMethod("risky"); m.invoke(obj); // underlying exception swallowed/misdiagnosed as generic failure try { m.invoke(obj); } catch (InvocationTargetException e) { Throwable actual = e.getCause(); // the real exception thrown by the invoked method // handle 'actual' appropriately }
Method.invoke() wraps any exception the invoked method throws in InvocationTargetException; the real cause must be retrieved via getCause(), or it's easy to misdiagnose the failure.
What is java.lang.Class, and what are the three common ways to obtain an instance of it?
Class describes a loaded type's structure at run time. You can obtain it via the .class literal (compile-time known type), calling getClass() on an instance (run-time actual type), or Class.forName(String) (dynamic lookup by fully-qualified name).
What is the difference between getFields()/getMethods() and getDeclaredFields()/getDeclaredMethods()?
getFields()/getMethods() return only public members, including inherited ones; getDeclaredFields()/getDeclaredMethods() return members declared directly on that class regardless of access modifier, but exclude inherited members.
What does setAccessible(true) do, and what risk does it introduce?
It suppresses Java's normal access-control checks for that Field/Method/Constructor, allowing reflective code to read/write private members or invoke non-public methods/constructors — which deliberately breaks the encapsulation the class author intended, and can be further restricted by the module system.
Why is reflective code generally slower than equivalent direct code?
Type and access checks that the compiler normally resolves statically must instead happen dynamically, lookups happen by name rather than through a resolved reference, and JIT optimization is typically less effective on reflective call paths than on direct ones.
How does Method.invoke() report an exception thrown by the invoked method?
It wraps the original exception in an InvocationTargetException; the actual underlying exception is retrieved via getCause() on that wrapper.
Name two categories of frameworks that fundamentally depend on reflection, and why.
Dependency injection frameworks (to instantiate and wire arbitrary classes based on configuration/annotations) and ORMs/serialization libraries (to map object fields to database columns or JSON generically, without per-class hand-written code).
What is a common pattern to reduce reflection's performance cost in framework code?
Perform reflective lookups (getMethod, getDeclaredField, etc.) once — typically at startup or on first use — and cache the resulting Method/Field/Constructor object for reuse, rather than repeating the lookup on every call.
Write a program that uses reflection to print the names and types of all public methods declared directly on a given class (excluding inherited ones).
import java.lang.reflect.Method; public class MethodLister { public static void main(String[] args) { for (Method m: String.class.getDeclaredMethods()) { if (java.lang.reflect.Modifier.isPublic(m.getModifiers())) { System.out.println(m.getReturnType().getSimpleName() + " " + m.getName()); } } } } getDeclaredMethods() returns methods declared directly on String (not inherited from Object), and filtering by Modifier.isPublic() narrows the output to only public ones, printing each method's return type and name.
What does this print?
99 — setAccessible(true) bypasses the private access restriction, letting reflective code both read and write the private field value directly, so setting it to 99 and reading it back returns 99.
Write a program that dynamically instantiates a class by fully-qualified name (using Class.forName) and calls a no-argument constructor and method on it.
import java.lang.reflect.Constructor; import java.lang.reflect.Method; class Greeter { public String greet() { return "Hello from Greeter!"; } } public class DynamicInvoke { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("Greeter"); Constructor<?> ctor = clazz.getDeclaredConstructor(); Object instance = ctor.newInstance(); Method greet = clazz.getMethod("greet"); System.out.println(greet.invoke(instance)); } }
Class.forName loads Greeter by name at run time; getDeclaredConstructor().newInstance() creates an instance reflectively, and getMethod("greet").invoke(instance) calls greet() on it dynamically, printing "Hello from Greeter!" without any compile-time reference to the Greeter type at the call site.
class Counter {
private int value = 5;
}
Field f = Counter.class.getDeclaredField("value");
f.setAccessible(true);
Counter c = new Counter();
f.set(c, 99);
System.out.println(f.get(c));