Java Reflection API
Java Reflection API
Every example so far has assumed you know, at compile time, exactly which classes, fields, and methods you are working with. Reflection breaks that assumption deliberately: it lets a running program inspect, and even modify, the structure of classes and objects it only discovers at runtime.
Introduction to Reflection
Reflection is the ability of a program to examine and manipulate its own structure, classes, methods, fields, constructors, while it is running. Every loaded class in Java has a single, corresponding java.lang.Class object describing it, and reflection works by querying that Class object.
Class<?> clazz1 = String.class; // via the .class literal
Class<?> clazz2 = "hello".getClass(); // via an existing object
Class<?> clazz3 = Class.forName("java.lang.String"); // via a fully qualified name, by String
System.out.println(clazz1 == clazz2); // true — same Class object, only one exists per type
Inspecting Classes at Runtime
Once you have a Class object, you can query nearly everything about the type it represents: its name, its superclass, the interfaces it implements, and every field, method, and constructor it declares.
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public void giveRaise(double amount) {
salary += amount;
}
}
Class<?> clazz = Employee.class;
System.out.println("Class name: " + clazz.getSimpleName());
System.out.println("Superclass: " + clazz.getSuperclass().getSimpleName());
for (Field field : clazz.getDeclaredFields()) {
System.out.println("Field: " + field.getName() + " (" + field.getType().getSimpleName() + ")");
}
for (Method method : clazz.getDeclaredMethods()) {
System.out.println("Method: " + method.getName());
}
Accessing Fields and Methods Dynamically
Beyond just listing fields and methods, reflection lets you read, write, and invoke them, by name, as plain strings, entirely bypassing the normal compile-time method call syntax.
Employee emp = new Employee("Asha", 50000);
Class<?> clazz = emp.getClass();
// Read a private field dynamically
Field salaryField = clazz.getDeclaredField("salary");
salaryField.setAccessible(true); // bypass private access for this reflective call
double currentSalary = (double) salaryField.get(emp);
System.out.println("Current salary: " + currentSalary);
// Invoke a method dynamically, by name and parameter types
Method raiseMethod = clazz.getDeclaredMethod("giveRaise", double.class);
raiseMethod.invoke(emp, 5000.0);
System.out.println("Updated salary: " + salaryField.get(emp)); // 55000.0
Creating Objects Using Reflection
Reflection can also construct entirely new objects, choosing the class to instantiate based on information only available at runtime, such as a class name read from a configuration file.
Class<?> clazz = Class.forName("com.app.models.Employee");
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, double.class);
Object newEmployee = constructor.newInstance("Ravi", 45000.0);
System.out.println(newEmployee.getClass().getSimpleName()); // Employee
This exact mechanism, reading a class name as a string and instantiating it via reflection, is how plugin systems, dependency injection frameworks, and many ORM (object-relational mapping) libraries dynamically wire together objects whose concrete types are only known from configuration, not hardcoded into the framework's own source.
Reflection and Encapsulation
setAccessible(true), used above to read a private field, deserves a moment of caution: it deliberately bypasses the access modifiers covered all the way back in Chapter 1 of the Intermediate tutorial. Reflection can, technically, read and even modify private fields from completely outside their class, something normal Java code can never do.
class SecretHolder {
private String secret = "classified";
}
SecretHolder holder = new SecretHolder();
Field secretField = SecretHolder.class.getDeclaredField("secret");
secretField.setAccessible(true);
System.out.println(secretField.get(holder)); // classified — encapsulation has been bypassed
Since Java 9's module system, this bypass is no longer unconditional: strongly encapsulated modules can refuse setAccessible(true) outright for fields outside their explicitly opened packages, throwing an InaccessibleObjectException instead. This was added specifically because unrestricted reflective access undermines the entire point of encapsulation if left completely unchecked.
Real-World Applications
Use Case | How Reflection Helps |
Dependency injection frameworks (Spring, etc.) | Instantiate classes and inject dependencies based purely on annotations, with no hardcoded class list |
Serialization libraries (Jackson, Gson) | Read and write an object's fields generically, without that object needing to implement any special interface |
Unit testing frameworks (JUnit, Mockito) | Discover and invoke test methods automatically, and create mock objects on the fly |
ORMs (Hibernate, JPA) | Map database columns to private object fields without requiring public getters and setters for every column |
Plugin and extension systems | Load and instantiate classes whose names are only known from a configuration file at startup |
Reflection Best Practices
- Avoid reflection for ordinary application logic; reach for it only when you genuinely need to operate on types unknown until runtime, since normal method calls are both faster and far easier to read.
- \Reflective calls are noticeably slower than direct method calls, since the JVM cannot apply many of its usual compile-time optimisations to them; avoid them in tight, performance-sensitive loops.
- Be cautious with setAccessible(true); bypassing encapsulation should be a deliberate, narrow exception, not a habit, and it may simply be blocked by the module system in modern Java.
- Wrap reflective calls in proper exception handling; many reflection methods throw checked exceptions like NoSuchMethodException or IllegalAccessException that must be handled explicitly.
- Prefer higher-level alternatives where they exist, method handles (java.lang.invoke) and modern dependency injection frameworks often achieve the same goals with better performance and clearer code than raw reflection.
Did You Know?
Almost every popular Java framework you have likely heard of, Spring, Hibernate, JUnit, relies on reflection somewhere near its core. Reflection is, in a very real sense, the technology that makes Java's framework ecosystem possible, even though application developers using those frameworks rarely write reflective code themselves.










