Reflection API
Inspect and control classes, methods, and fields while your program is running. By the end you'll read fields, invoke methods by name, build objects without new , and understand why this powers Spring, Hibernate, and JUnit — and when to leave it alone.
Learn Reflection API in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
This is an advanced topic. You should be comfortable with classes and objects , access modifiers like private , and the basics of annotations . Reflection works at the JVM level, deliberately bypassing the compile-time checks those features rely on — so understanding the normal rules first makes it clear what reflection is breaking.
💡 Analogy: Normally you use an object like a TV remote — you press the labelled buttons and never think about the circuit board inside. Reflection is prising the case open. Now you can see every chip and wire, read values the buttons never exposed, and even solder new connections at runtime. It's powerful, but you've voided the warranty: the manufacturer's safety checks no longer protect you, and it's easy to break something.
That's the trade-off in one sentence. Code written before your class even existed can work with it — that's how Spring creates your beans, Hibernate maps your entities, and JUnit finds your @Test methods. But you give up the compiler's protection, you pay a speed cost, and you reach past private walls that were there for a reason.
Every reflection task begins by getting a Class object — a runtime description of a type. The angle-bracket version Class ? just means "a Class of some type I'm not naming". There are three ways to get one, and they all return the same object for a given type.
Once you hold a Class object, you can ask it questions: its name, its fields, its methods, its constructors, its annotations.
With a Class object in hand you can do four core things. The example below does all four on a small User class:
Notice setAccessible(true) in the example. getDeclaredField / getDeclaredMethod can find private members, but the JVM still blocks access until you explicitly switch the check off. That single call is what lets the code read the secret field and call privateMethod() .
Fill in the three blanks so the program reads the private price field of a Product . You need the field's name as a String, a call to switch off the access check, and a read with the object passed in.
This is reflection's killer feature. An annotation like @Test is just metadata attached to a method. If the annotation is declared @Retention(RetentionPolicy.RUNTIME) , reflection can see it while the program runs and react to it.
That's the entire idea behind JUnit: loop over a class's methods, keep only the ones where m.isAnnotationPresent(Test.class) is true, and invoke each. Spring does the same with @Component ; Jackson with @JsonProperty . The example below is a working 30-line test runner.
Fill in the blanks so the program looks up the hello method (which takes one String ) and calls it on object g with the argument "World" .
Now with the scaffolding removed. You're given an Account whose deposit method is private . Using only the comment outline below, reflectively call deposit(50.0) and print the new balance. Everything you need is in the worked examples above.
💡 Cache everything. Look up Method and Field objects once at startup and reuse them — the lookup is the expensive part, not the invoke.
💡 Prefer MethodHandle ( java.lang.invoke , Java 7+) for performance-sensitive reflective calls — it's JIT-friendly and far closer to direct-call speed.
💡 Prefer compile-time tools like annotation processors (Lombok, MapStruct) when you can — they generate ordinary code with zero runtime reflection cost.
💡 Proxy.newProxyInstance() builds an interface implementation at runtime — the foundation of Spring AOP and dynamic mocks.
You can now get a Class object three ways, inspect and read fields, invoke methods by name, build objects without new , and react to annotations at runtime — the exact machinery inside Spring, Hibernate, and JUnit. Just as importantly, you know its costs: it's slower, it breaks encapsulation, and it's fragile to refactors, so you reach for it only when you truly can't know the types in advance.
Next up: Annotations — how to create your own metadata and process it, the perfect companion to the reflection you just learned.
Practice quiz
Which way of getting a Class object works when the type name is only a String at runtime?
- obj.getClass()
- String.class
- Class.forName("java.lang.String")
- new String().class
Answer: Class.forName("java.lang.String"). Class.forName("...") loads a class by its fully qualified name — the most dynamic option, used by frameworks.
Do obj.getClass(), String.class, and Class.forName("java.lang.String") return the same Class object?
- Yes, all three are the identical Class instance for that type
- No, three different objects
- Only the first two are equal
- Only at compile time
Answer: Yes, all three are the identical Class instance for that type. There is one Class object per type, so all three resolve to the same instance (c1 == c2 == c3).
Which method lists every field a class declares, including private ones?
- getFields()
- getPublicFields()
- fields()
- getDeclaredFields()
Answer: getDeclaredFields(). getDeclaredFields() returns all fields declared by the class (including private); getFields() returns only public ones.
After getDeclaredField("price") on a private field, what must you call before reading it?
- field.open()
- field.setAccessible(true)
- field.unlock()
- field.makePublic()
Answer: field.setAccessible(true). getDeclaredField finds the field, but the JVM blocks access until you call setAccessible(true).
How do you read a field's value from a specific object?
- field.get(obj)
- field.read(obj)
- field.value()
- field.invoke(obj)
Answer: field.get(obj). field.get(obj) returns that field's value from the given object.
Which call is the reflective equivalent of obj.greet()?
- clazz.callMethod("greet", obj)
- obj.reflect("greet")
- clazz.getMethod("greet").invoke(obj)
- Method.run(obj, "greet")
Answer: clazz.getMethod("greet").invoke(obj). Look up the Method with getMethod("greet"), then call method.invoke(obj) to dispatch it dynamically.
How do you create an object without the 'new' keyword via reflection?
- clazz.create()
- constructor.newInstance(args)
- clazz.makeNew()
- Method.invoke(null)
Answer: constructor.newInstance(args). Get a Constructor (e.g. getDeclaredConstructor(...)) and call newInstance(args) to build the object.
For reflection to see an annotation at runtime, what retention must it have?
- RetentionPolicy.SOURCE
- RetentionPolicy.CLASS
- Any retention works
- RetentionPolicy.RUNTIME
Answer: RetentionPolicy.RUNTIME. Only @Retention(RetentionPolicy.RUNTIME) annotations are visible to reflection; the default CLASS retention is not.
When a reflectively-invoked method throws, what exception do you catch, and how do you get the real cause?
- RuntimeException; getMessage()
- InvocationTargetException; e.getCause()
- ReflectiveOperationException; e.toString()
- IllegalAccessException; e.getStackTrace()
Answer: InvocationTargetException; e.getCause(). invoke wraps the real error in InvocationTargetException; unwrap it with e.getCause() to see the original exception.
Which is a real downside of reflection?
- It is type-safe at compile time
- It cannot read private members
- It is slower, breaks encapsulation, and method-name typos only fail at runtime
- It only works on interfaces
Answer: It is slower, breaks encapsulation, and method-name typos only fail at runtime. Reflection is slower, bypasses compile-time checks (string names fail at runtime), and is fragile to refactors.