class Student {
private String name;
public void study() {
System.out.println("Student is studying");
}
}Java Reflection API: Inspecting Classes, Methods, and Modifying Fields at Runtime
Direct Answer & Core Concept
Java Reflection API allows a Java program to inspect classes, methods, constructors, fields, interfaces, and annotations while the application is running. Instead of knowing everything about a class at compile time, reflection lets a program discover information dynamically.
Reflection is widely used behind the scenes by frameworks and libraries. Dependency injection frameworks, object-relational mappers, testing tools, serialization libraries, and plugin systems can use reflection to discover classes and invoke their members.
In this tutorial, you will learn how to inspect classes, find methods and fields, create objects dynamically, invoke methods, access fields, inspect annotations, and understand the limitations and risks of Java reflection.
Detailed Explanation & Mechanics
What Is Reflection in Java?
Reflection is the ability of a running Java program to examine and interact with its own classes and objects.
For example, suppose you have:
Normally, you access the class directly:
Student student = new Student();
student.study();With reflection, you can discover information about the class dynamically:
Class<?> clazz = Student.class;
System.out.println(clazz.getName());You can also inspect its fields and methods without manually listing them in your source code.
The Class Object
The central type in Java reflection is:
java.lang.ClassEvery Java class has a corresponding Class object at runtime.
There are several common ways to obtain it.
Using .class
Class<?> clazz = Student.class;Using an object's getClass()
Student student = new Student();
Class<?> clazz = student.getClass();Using Class.forName()
Class<?> clazz = Class.forName("com.example.Student");The third approach is useful when the class name is available dynamically as a string.
Example: Inspecting a Class
Consider this class:
class Employee {
private String name;
private int age;
public void work() {
System.out.println("Working...");
} public void display() {
System.out.println(name + " " + age);
}
}You can inspect it using:
public class Main {
public static void main(String[] args) {
Class<?> clazz = Employee.class;
System.out.println("Class name: " + clazz.getName());
System.out.println("Simple name: " + clazz.getSimpleName());
}
}Possible output:
Class name: EmployeeSimple name: EmployeeFor a class inside a package, getName() includes the package name.
getName() vs getSimpleName()
These methods provide different forms of the class name.
Class<?> clazz = Employee.class;
System.out.println(clazz.getName());
System.out.println(clazz.getSimpleName());For:
package com.example;the output can be:
com.example.EmployeeEmployeeUse getSimpleName() when you only need the class's simple name.
Inspecting the Superclass
Reflection can also tell you which class a class extends.
Class<?> clazz = Employee.class;
Class<?> parent = clazz.getSuperclass();
System.out.println(parent.getName());If Employee does not explicitly extend another class, its direct superclass is normally:
java.lang.ObjectThis can be useful when building class inspection or framework tools.
Inspecting Implemented Interfaces
You can retrieve the interfaces directly implemented by a class using:
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> interfaceClass : interfaces) {
System.out.println(interfaceClass.getName());
}Example:
interface Printable {
}class Report implements Printable {
}Reflection:
Class<?> clazz = Report.class;
for (Class<?> interfaceClass : clazz.getInterfaces()) {
System.out.println(interfaceClass.getSimpleName());
}Output:
PrintableInspecting Class Modifiers
Reflection can inspect modifiers such as:
publicprivateprotectedstaticfinalabstract
Example:
import java.lang.reflect.Modifier;
Class<?> clazz = Employee.class;
int modifiers = clazz.getModifiers();
System.out.println( Modifier.toString(modifiers));The Modifier utility class converts the numeric modifier representation into readable text.
Inspecting Fields
A field represents a variable declared in a class.
Suppose:
class Employee {
private String name;
private int age;
public double salary;
}Reflection can inspect the fields.
Field[] fields = Employee.class.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}Possible output:
nameagesalaryYou need to import:
import java.lang.reflect.Field;getFields() vs getDeclaredFields()
These two methods are important and behave differently.
getFields()
clazz.getFields();Returns accessible public fields, including inherited public fields.
getDeclaredFields()
clazz.getDeclaredFields();Returns fields declared directly by that class, including private and protected fields.
It does not automatically include fields declared in superclasses.
This distinction is important when writing reflection-based frameworks.
Inspecting a Field's Type
Reflection can determine the data type of a field.
Field field = Employee.class .getDeclaredField("age");
System.out.println(field.getName());
System.out.println(field.getType().getName());Possible output:
ageintFor an object field:
private String name;the type is:
java.lang.StringReading a Field Value
Reflection can read a field from an object.
Consider:
class Employee {
private String name = "Amit";
}You can access it reflectively:
Employee employee = new Employee();
Field field = Employee.class.getDeclaredField("name");
field.setAccessible(true);
Object value = field.get(employee);
System.out.println(value);Output:
AmitThe Field.get() method receives the object whose field should be read.
What Does setAccessible(true) Do?
Normally, Java access rules prevent direct access to a private field.
For example:
private String name;Code outside the class cannot normally access it directly.
Reflection provides access-control mechanisms that can allow reflective access in situations where the runtime/module/security configuration permits it.
A common older-style example is:
field.setAccessible(true);However, modern Java's strong encapsulation and module system can restrict reflective access to members of strongly encapsulated modules. In such cases, setAccessible(true) can fail rather than simply bypassing every access restriction.
For application code, prefer normal accessors or supported APIs when possible.
Modifying a Field at Runtime
Reflection can also modify a field value.
Example:
class Employee {
private String name = "Amit";
}Then:
Employee employee = new Employee();
Field field = Employee.class.getDeclaredField("name");
field.setAccessible(true);
field.set(employee, "Rahul");
System.out.println(field.get(employee));Output:
RahulThe object's field was changed through reflection rather than through a normal setter.
Complete Field Reflection Example
import java.lang.reflect.Field;
class Employee {
private String name = "Amit";
private int age = 25;
}public class Main {
public static void main(String[] args) throws Exception {
Employee employee = new Employee();
Class<?> clazz = employee.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
System.out.println( "Field: " + field.getName());
System.out.println( "Type: " + field.getType().getSimpleName());
System.out.println( "Value: " + field.get(employee));
}
}
}Possible output:
Field: nameType: StringValue: AmitField: ageType: intValue: 25Inspecting Methods
Reflection can discover methods declared by a class.
Method[] methods = Employee.class.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}Import:
import java.lang.reflect.Method;For example:
class Employee {
public void work() {
} private void calculateSalary() {
}
}getDeclaredMethods() can discover both methods because it returns methods declared directly by the class regardless of their access modifier.
getMethods() vs getDeclaredMethods()
Just like fields, methods have two commonly used reflection methods.
getMethods()
clazz.getMethods();Returns public methods, including inherited public methods.
getDeclaredMethods()
clazz.getDeclaredMethods();Returns methods declared directly by the class, including methods that are not public.
It does not automatically return methods declared by parent classes.
Inspecting Method Parameters
Reflection can inspect a method's parameter types.
Suppose:
class Calculator {
public int add(int a, int b) {
return a + b;
}
}You can inspect it:
Method method = Calculator.class.getMethod( "add", int.class, int.class);
Class<?>[] parameters = method.getParameterTypes();
for (Class<?> parameter : parameters) {
System.out.println( parameter.getSimpleName());
}Output:
intintInvoking a Method With Reflection
Reflection can invoke a method dynamically.
Example:
class Calculator {
public int add(int a, int b) {
return a + b;
}
}Invocation:
Calculator calculator = new Calculator();
Method method = Calculator.class.getMethod( "add", int.class, int.class);
Object result = method.invoke(calculator, 10, 20);
System.out.println(result);Output:
30The first argument to invoke() is the target object.
The remaining arguments are passed to the method.
Invoking a Private Method
Reflection can sometimes access a private method when the runtime permits reflective access.
Example:
class Secret {
private void showMessage() {
System.out.println("Private method");
}
}Reflection:
Secret object = new Secret();
Method method = Secret.class.getDeclaredMethod( "showMessage");
method.setAccessible(true);
method.invoke(object);Output:
Private methodAs with private fields, access can be restricted by modern Java module boundaries and runtime configuration.
Reflection Exceptions
Reflection APIs commonly work with checked exceptions.
For example:
Class.forName(...)can throw:
ClassNotFoundExceptionFinding a field can throw:
NoSuchFieldExceptionFinding a method can throw:
NoSuchMethodExceptionInvoking a method can throw:
InvocationTargetExceptionIllegalAccessExceptionAccessing fields can also involve:
IllegalAccessExceptionA robust application should handle these exceptions rather than relying on:
throws Exceptioneverywhere.
Understanding InvocationTargetException
When a reflectively invoked method itself throws an exception, reflection commonly wraps that exception in:
InvocationTargetExceptionExample:
try {
method.invoke(object);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
cause.printStackTrace();
}getCause() can help you inspect the exception actually thrown by the target method.
Inspecting Constructors
Reflection can also inspect constructors.
Example:
Constructor<?>[] constructors = Employee.class.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}Import:
import java.lang.reflect.Constructor;You can inspect parameter types:
for (Constructor<?> constructor : Employee.class.getDeclaredConstructors()) {
System.out.println( constructor.getParameterCount());
}Creating Objects With Reflection
A constructor can be obtained and invoked dynamically.
Suppose:
class Employee {
private String name;
public Employee(String name) {
this.name = name;
}
}Reflection:
Constructor<Employee> constructor = Employee.class.getConstructor(String.class);
Employee employee = constructor.newInstance("Rahul");Now:
System.out.println(employee);represents an object created dynamically through reflection.
Class.forName() and Dynamic Classes
One powerful reflection feature is loading a class when its name is available as a string.
String className = "java.lang.String";
Class<?> clazz = Class.forName(className);
System.out.println(clazz.getName());This is useful in situations where class names come from configuration or plugin metadata.
For example:
config file↓class name↓Class.forName()↓Class object↓reflectionThis technique is common in framework infrastructure, although modern dependency injection and module systems often provide higher-level mechanisms.
Inspecting Annotations
Reflection can inspect annotations that are available at runtime.
Example:
@Retention(RetentionPolicy.RUNTIME)@interface Author {
String name();
}Use it:
@Author(name = "Amit")class Employee {
}Inspect it:
Author annotation = Employee.class.getAnnotation(Author.class);
if (annotation != null) {
System.out.println( annotation.name());
}Output:
AmitThis is one reason runtime-retained annotations are useful in frameworks.
Reflection and Interfaces
You can check whether a class implements a particular interface.
Class<?> clazz = Employee.class;
boolean result = Runnable.class.isAssignableFrom(clazz);
System.out.println(result);Another common check is:
clazz.isInterface();You can also check:
clazz.isEnum();
clazz.isArray();
clazz.isAnnotation();
clazz.isRecord();These methods help tools understand what kind of class they are inspecting.
Checking Class Relationships
Reflection provides several methods for understanding type relationships.
For example:
Class<?> clazz = Employee.class;
System.out.println( clazz.isAssignableFrom(Object.class));Be careful with the direction of isAssignableFrom().
For example:
Object.class.isAssignableFrom(Employee.class)is true because an Employee can be assigned to an Object reference.
This method is useful when frameworks need to determine whether one type is compatible with another.
Reflection With Arrays
Reflection can also inspect arrays.
int[] numbers = {
10, 20, 30
};
Class<?> clazz = numbers.getClass();
System.out.println(clazz.isArray());
System.out.println(clazz.getComponentType());Output conceptually:
trueintFor:
String[] names;the component type is:
java.lang.StringReflection With Generic Types
Java reflection can expose generic type information in places where it is retained in class metadata.
For example:
class Data {
private java.util.List<String> names;
}You can inspect:
Field field = Data.class.getDeclaredField("names");
System.out.println( field.getGenericType());This can provide more information than:
field.getType()The distinction matters because generic type arguments are largely erased at runtime, but certain generic declarations are retained in class metadata and can be inspected reflectively.
Reflection and Type Erasure
Java generics use type erasure.
For example:
List<String>and:
List<Integer>are represented using the same raw runtime class:
ListTherefore, this is not generally possible:
if (object instanceof List<String>) {
}But reflection can inspect generic declarations recorded in class metadata.
This distinction is important:
Runtime object type ≠Complete generic type informationA Practical Reflection-Based Object Inspector
Reflection can be used to build a simple object inspector.
import java.lang.reflect.Field;
class Employee {
private String name = "Rahul";
private int age = 30;
}public class Main {
public static void inspect(Object object) throws IllegalAccessException {
Class<?> clazz = object.getClass();
System.out.println( "Class: " + clazz.getName());
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(object);
System.out.println( field.getName() + " = " + value);
}
} public static void main(String[] args) throws Exception {
inspect(new Employee());
}
}Possible output:
Class: Employeename = Rahulage = 30This small example demonstrates the basic idea behind many reflection-based inspection tools.
How Frameworks Use Reflection
Reflection is especially useful when a framework needs to work with classes that the framework did not explicitly code against.
For example:
Application Classes↓Framework↓Reflection↓Discover fieldsDiscover methodsDiscover annotationsCreate objectsInvoke methodsA dependency injection framework may discover a constructor or annotation.
An ORM may inspect fields or methods to map object properties to database columns.
A testing framework may discover methods marked with test annotations.
A serialization framework may inspect fields and annotations to determine how an object should be represented.
Common Real-World Uses of Reflection
Reflection can be found in areas such as:
Dependency injection
Object-relational mapping
Testing frameworks
Serialization frameworks
Plugin systems
Annotation processing at runtime
Object inspectors
Configuration frameworks
Java application servers
Generic utility libraries
Reflection is usually infrastructure technology rather than something every application class needs to use directly.
Advantages of Reflection
Dynamic Behavior
Reflection allows programs to discover classes and members at runtime instead of requiring every type to be known statically.
Framework Development
It is useful when building generic frameworks that need to operate across many unrelated classes.
Plugin Architecture
Applications can dynamically load classes and inspect their capabilities.
Annotation-Based Configuration
Runtime annotations can be inspected and used to drive framework behavior.
Disadvantages of Reflection
Reduced Type Safety
Normal Java code is checked strongly by the compiler.
Reflection moves some decisions to runtime.
For example:
method.invoke(object, value);may fail only when the code executes.
More Complex Code
Reflection generally requires additional exception handling and runtime checks.
Performance Overhead
Reflective operations can have additional overhead compared with direct method or field access. Modern JVMs can optimize many workloads, but direct calls should generally be preferred when dynamic behavior is not required.
Encapsulation Concerns
Reflection can interact with private members, but modern Java's module system can restrict access to strongly encapsulated code.
Harder Debugging
A typo in a class, method, or field name can become a runtime error instead of a compile-time error.
For example:
getDeclaredField("nam");instead of:
getDeclaredField("name");can cause:
NoSuchFieldExceptionReflection vs Normal Java Code
| Feature | Normal Code | Reflection |
|---|---|---|
| Type checking | Mostly compile time | More runtime checking |
| Method calls | Direct | Dynamic |
| Field access | Direct | Dynamic |
| Performance | Usually simpler/faster | Can have additional overhead |
| Flexibility | Lower | Higher |
| Code complexity | Usually lower | Usually higher |
| Framework usage | Limited | Very common |
| Errors | Often compiler-detected | Often runtime exceptions |
Important Reflection Classes
The most commonly used reflection classes include:
java.lang.Classjava.lang.reflect.Fieldjava.lang.reflect.Methodjava.lang.reflect.Constructorjava.lang.reflect.Modifierjava.lang.reflect.ParameterThese classes provide the building blocks for runtime inspection.
Important Reflection Methods
Class information
getName()getSimpleName()getSuperclass()getInterfaces()getModifiers()Fields
getFields()getDeclaredFields()getField()getDeclaredField()Methods
getMethods()getDeclaredMethods()getMethod()getDeclaredMethod()Constructors
getConstructors()getDeclaredConstructors()getConstructor()getDeclaredConstructor()Object interaction
Field.get()Field.set()Method.invoke()Constructor.newInstance()Best Practices for Java Reflection
Prefer Normal APIs When Possible
If you already know the class and method at compile time, direct Java code is usually clearer.
Prefer:
employee.getName();over reflection when dynamic behavior is unnecessary.
Keep Reflection at the Boundary
A framework or utility layer can isolate reflection from the rest of the application.
This keeps business logic easier to understand and maintain.
Validate Types
Before setting or invoking something dynamically, verify that the value and target types are compatible.
Handle Exceptions Properly
Reflection can produce several runtime and checked exceptions. Handle them according to the application's needs.
Avoid Breaking Encapsulation Unnecessarily
Do not use reflection to access private implementation details merely because it is possible.
Consider Modern Alternatives
For some dynamic invocation requirements, MethodHandle or other Java APIs may provide a better fit than traditional reflection.
Reflection and Security
Reflection deserves special attention when used with external input.
For example, avoid allowing arbitrary user input to determine:
Class.forName(userInput);without strong validation.
Otherwise, an application can unintentionally expose dynamic class loading or invocation behavior that was never intended.
Similarly, avoid dynamically invoking arbitrary methods based directly on untrusted request parameters.
A safer design usually maps external values to an explicitly allowed set of classes or operations.
Reflection and private Members
Reflection does not mean that Java's access rules simply disappear.
For example:
private String password;is intentionally hidden from normal callers.
Modern Java also has module boundaries and strong encapsulation mechanisms that can prevent reflective access to certain members.
Therefore, code such as:
field.setAccessible(true);should not be treated as a universal "bypass private" mechanism.
Use supported public APIs whenever they are available.
Reflection and final Fields
Reflection has historically allowed unusual manipulation of fields, but modifying final fields is heavily constrained and should not be treated as a normal programming technique.
If an object is designed to be immutable, do not depend on reflection to alter its internal state.
Reflection should generally inspect or interact with types according to their intended API rather than deliberately bypassing their design.
Reflection Example: Calling a Method Dynamically
Here is a compact example that combines class inspection and method invocation:
import java.lang.reflect.Method;
class Greeting {
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}public class Main {
public static void main(String[] args) throws Exception {
Greeting greeting = new Greeting();
Class<?> clazz = greeting.getClass();
Method method = clazz.getMethod( "sayHello", String.class);
method.invoke(greeting, "Rahul");
}
}Output:
Hello, RahulThe method name was resolved dynamically.
Reflection Example: Dynamically Changing a Field
import java.lang.reflect.Field;
class Product {
private String name = "Laptop";
}public class Main {
public static void main(String[] args) throws Exception {
Product product = new Product();
Field field = Product.class.getDeclaredField("name");
field.setAccessible(true);
System.out.println( "Before: " + field.get(product));
field.set(product, "Phone");
System.out.println( "After: " + field.get(product));
}
}Output:
Before: LaptopAfter: PhoneThis demonstrates the ability of reflection to inspect and modify an object's field at runtime when access is permitted.
Frequently Asked Questions
What is Reflection API in Java?
Java Reflection API allows a running Java program to inspect classes, fields, methods, constructors, annotations, and other type information dynamically.
Which class is the foundation of Java reflection?
java.lang.Class is the central class used to represent runtime class information.
How do I get a class object?
Common approaches include:
Employee.classemployee.getClass()Class.forName("com.example.Employee")How do I inspect private fields?
getDeclaredFields() can discover fields declared by the class. Reflective access to private fields may require additional access permissions and can be restricted by modern Java encapsulation.
How do I invoke a method using reflection?
Use Method.invoke():
method.invoke(object, arguments);How do I create an object using reflection?
A constructor can be obtained and invoked with:
constructor.newInstance(arguments);Is reflection slower than normal method calls?
Reflection can introduce additional overhead and runtime checks. For code that does not require dynamic behavior, direct calls are generally preferable.
Is reflection dangerous?
Reflection itself is a normal Java feature, but careless use can weaken encapsulation, create runtime failures, and expose security risks when class names or methods are controlled by untrusted input.
Where is reflection commonly used?
It is widely used by frameworks for dependency injection, testing, serialization, ORM, configuration, plugin systems, and annotation-driven behavior.
Java Reflection Cheat Sheet
Class↓Runtime information about a typeField↓Inspect/read/write fieldsMethod↓Inspect/invoke methodsConstructor↓Inspect/create objectsAnnotation↓Inspect runtime metadataReflection↓Dynamic runtime behaviorMost important examples
Class<?> clazz = object.getClass();
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
Object value = field.get(object);
field.set(object, "New Value");Method invocation:
Method method = clazz.getDeclaredMethod( "hello", String.class);
method.setAccessible(true);
method.invoke(object, "Java");Constructor:
Constructor<?> constructor = clazz.getDeclaredConstructor( String.class);
Object object = constructor.newInstance("Java");Suggested Graphics and Page Design
Graphic 1 — Reflection Overview
Java Program
│
▼
Class Object
│
┌───────────┼───────────┐
▼ ▼ ▼
Fields Methods Constructors
│ │ │
▼ ▼ ▼
get/set invoke newInstanceGraphic 2 — Runtime Inspection
Show a Java class on the left and a reflection inspector on the right:
Employee
├── name : String
├── age : int
├── work()
└── display()
↓
Reflection
Class
Field[]
Method[]
Constructor[]Graphic 3 — Dynamic Method Invocation
Method Name↓getDeclaredMethod()↓Method Object↓invoke()↓Target ObjectGraphic 4 — Reflection Use in Frameworks
Application Class↓Annotations↓Reflection API↓Framework↓Object Creation / Method Calls / Field MappingUse clean diagrams with Java-style syntax, simple boxes, arrows, and minimal decoration. Avoid generic stock images; technical diagrams will explain this topic much better.
Runnable Code Example: Java Reflection API
Java 17+import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionDemo {
private String secretKey = "SECRET_12345";
public static void main(String[] args) throws Exception {
ReflectionDemo instance = new ReflectionDemo();
Field field = ReflectionDemo.class.getDeclaredField("secretKey");
field.setAccessible(true);
// Bypass private encapsulation
System.out.println("Reflected Field Value: " + field.get(instance));
}
}