Reflection lets a Java program inspect classes, constructors, methods, and fields while it runs. It is useful in frameworks, test tools, serializers, and diagnostic utilities when types are discovered dynamically. Ordinary direct calls remain clearer when you already know the type at compile time. Reflection replaces some compile-time checking with runtime lookup, so you must handle missing members, access restrictions, and exceptions from invoked code explicitly.
Start from a Class object
Every loaded reference type has a Class representation. Use SomeType.class when the type is known, object.getClass when inspecting an instance, or Class.forName when a configured class name must be resolved. The first two forms keep a compile-time connection to the type. Class.forName can fail with ClassNotFoundException and may initialize the class. Do not accept arbitrary class names from untrusted input and treat loading them as harmless text processing.
Find public versus declared members
getMethod searches public methods, including inherited ones, using the method name and parameter types. getDeclaredMethod searches methods declared directly on the class, including nonpublic methods, but finding one does not automatically grant permission to call it. Similar distinctions exist for fields and constructors. A lookup for greet(String.class) is different from greet(Object.class), even when a String could be passed to an Object parameter in a direct call.
Construct an object explicitly
Use getConstructor or getDeclaredConstructor to identify the constructor you need, then newInstance to invoke it. The constructor receives ordinary arguments with runtime compatibility checks. Avoid the older Class.newInstance API, which has been deprecated. Keep the created object behind a known interface when possible, so subsequent work can use normal method calls. Reflect only the discovery step instead of making every operation depend on fragile string names.
Invoke a method and handle failures
Method.invoke receives a target instance for an instance method, followed by its arguments. A static method does not need a target instance. If the invoked method throws, reflection wraps that failure in InvocationTargetException; its cause contains the original exception. Missing methods and illegal access are separate reflection failures. Distinguishing these categories helps diagnose whether the configuration is wrong, access is forbidden, or the requested operation itself failed.
Respect encapsulation and module boundaries
Private fields are implementation details, not a public contract for reflection clients. setAccessible or trySetAccessible may be restricted, particularly across module boundaries in modern Java. Bypassing access can also break when an internal field is renamed. Prefer getters, documented interfaces, or a framework's supported integration. These examples inspect public members and do not require opening JDK internals or weakening module checks to run.
Limit reflective work to useful boundaries
Reflection can make refactoring harder because a compiler cannot update string member names as reliably as ordinary references. It can also add discovery and invocation overhead, although performance depends on actual use. Cache verified member information for repeated framework operations when appropriate, and report configuration errors early. Never introduce reflection merely to avoid writing a straightforward call; its flexibility is valuable when the program truly does not know the type beforehand.
Practice tasks and checks
Add a second public Receipt method taking a String argument and discover it using String.class. Invoke it with a matching instance and input, then deliberately request a method name that does not exist. Handle that lookup error separately from the divide invocation error. Next, compare getMethod and getDeclaredMethod on a subclass inheriting heading: the public inherited method is found by the former, while the latter searches only the subclass's own declarations. Keep private fields untouched. This practice establishes the distinction between member discovery, access permission, argument compatibility, and an exception thrown by the target method. Each failure should have a clear explanation rather than a single generic catch that hides its cause.
Java Summary
- Reflection lookup uses the declared parameter types.
- InvocationTargetException wraps an exception thrown by the target method.
- Prefer ordinary typed calls when the type is already known.
- Continue with annotation metadata when these examples are clear.
Java Examples
Each example shows the code, the output it produces and what each part does.
Inspect a public method
Save this program as MethodInspection.java. Compile it with javac MethodInspection.java and run it with java MethodInspection.
Expected output:
The lookup specifies int.class because title receives a primitive int. Integer.class would request a different signature and fail here. The Method object reports the declaration's name and return type without invoking the method. Printing simple names keeps the output concise; getName on the return type would provide its fully qualified name. Inspection alone does not construct a Receipt or generate a title.
import java.lang.reflect.Method;
public class MethodInspection {
public static class Receipt {
public String title(int number) { return "Receipt " + number; }
}
public static void main(String[] args) throws Exception {
Method method = Receipt.class.getMethod("title", int.class);
System.out.println(method.getName());
System.out.println(method.getReturnType().getSimpleName());
}
}title
StringConstruct and invoke a discovered member
Save this program as ReflectiveReceipt.java. Compile it with javac ReflectiveReceipt.java and run it with java ReflectiveReceipt.
Expected output:
The public constructor accepts a String and stores it in the new instance. The method lookup then requests heading with its primitive int parameter. invoke receives the created receipt as its target and eighteen as the argument. The reflected result is returned as Object and printed. The private shop field remains private; the program uses the public constructor and method instead of modifying that field reflectively.
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectiveReceipt {
public static class Receipt {
private final String shop;
public Receipt(String shop) { this.shop = shop; }
public String heading(int number) { return shop + " #" + number; }
}
public static void main(String[] args) throws Exception {
Constructor<Receipt> constructor = Receipt.class.getConstructor(String.class);
Receipt receipt = constructor.newInstance("Corner Books");
Method heading = Receipt.class.getMethod("heading", int.class);
System.out.println(heading.invoke(receipt, 18));
}
}Corner Books #18Recover the underlying invocation error
Save this program as ReflectionFailure.java. Compile it with javac ReflectionFailure.java and run it with java ReflectionFailure.
Expected output:
The method exists and is accessible, so discovery succeeds. The actual failure occurs inside divide when the divisor is zero. Reflection wraps that ArithmeticException in InvocationTargetException, and getCause exposes it. Catching only NoSuchMethodException would not handle this case because the lookup was valid. A real framework should preserve useful context and the original cause when reporting such failures.
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionFailure {
public static class Divider {
public int divide(int divisor) { return 12 / divisor; }
}
public static void main(String[] args) throws Exception {
Method divide = Divider.class.getMethod("divide", int.class);
try {
divide.invoke(new Divider(), 0);
} catch (InvocationTargetException error) {
System.out.println(error.getCause().getClass().getSimpleName());
}
}
}ArithmeticExceptionJava FAQ
Why use int.class instead of Integer.class in lookup?
Reflection looks for the declared parameter types. A method declared with int has a primitive parameter, so its signature is identified with int.class. Boxing that may occur during invocation does not change the declaration used for lookup.
Can reflection always read private JDK fields?
No. Access rules and module encapsulation can prohibit it. Design against supported public APIs instead of assuming setAccessible will work for any class on every Java version.
Is reflection slow?
A reflective call does more work than a direct call: it looks up members, checks access and boxes arguments. Look up Method and Field objects once and reuse them. For most framework start-up work the cost is small, but avoid reflection in tight loops that run millions of times.
When should I avoid reflection?
In ordinary application code. Reflection skips compile-time checks, so renaming a method breaks string-based lookups only when the program runs, and IDE refactoring tools can't follow it. Prefer interfaces, lambdas or a small Map of handlers; leave reflection to frameworks, serialisation libraries and test tools.
What are method handles?
java.lang.invoke.MethodHandle is a newer, typed way to call methods and access fields found at run time. Method handles are checked when they are created rather than on every call and can be optimised well by the JVM, so libraries often prefer them over classic reflection for repeated calls.
Java Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Which lookup includes public inherited methods?
What wraps an exception thrown by a reflected method?
Java Quiz
2 questions to check what you learned.
What is the target argument to invoke for an instance method?
- The instance on which the method should run
- The method name again
- The ClassLoader only
Which is preferable when the type is already known?
- An ordinary typed method call
- Reflection using string names for every call
- Reading private fields first