Java Reflection Introduction
What reflection is in Java, when to use it, and an overview of the java.lang.reflect package.
Java Reflection Introduction
Reflection is the ability of a program to inspect and manipulate its own structure at runtime: to ask a class what fields, methods, and constructors it has, to read and write those fields, to call those methods, and to create new instances — all without naming the types at compile time. Where ordinary Java is static (the compiler knows every type you touch), reflection is dynamic (you discover types from strings, configuration, or whatever is loaded at runtime). This part of the book is a tour of java.lang.reflect; this chapter sets the scene.
What reflection lets you do
Normal code names the type it works with:
User u = new User("ada");
String name = u.getName();Reflective code reaches the same result without writing User or getName as compile-time tokens — they're strings resolved at runtime:
Class<?> cls = Class.forName("com.example.User");
Object u = cls.getDeclaredConstructor(String.class).newInstance("ada");
Object name = cls.getMethod("getName").invoke(u);The second form is far more verbose and far slower, and it discards compile-time type checking. You'd never write it for ordinary application logic. You reach for it precisely when the type isn't known until runtime.
When reflection earns its keep
Reflection is the engine behind frameworks, not everyday code. Typical uses:
- Dependency injection (Spring, Guice, CDI): the container reads annotations and constructor signatures, then instantiates and wires beans it has never seen the source for.
- Serialization (Jackson, Gson): a JSON library walks an object's fields to read or populate them without you writing per-class mapping code.
- ORMs (Hibernate, JPA): map columns to fields by reflecting over an entity class.
- Test runners (JUnit): find methods annotated
@Testand invoke them. - Plugin systems: load a class named in a config file and call a known interface method on it.
The common thread: a general mechanism operating over arbitrary user types it was not compiled against. That is exactly the problem reflection solves.
The core types in java.lang.reflect
Reflection starts from a Class object (covered in the next chapter) and branches into a small family of classes, each describing one kind of member:
| Type | Represents | Got from Class via |
|---|---|---|
Field | a field | getField / getDeclaredField(s) |
Method | a method | getMethod / getDeclaredMethod(s) |
Constructor<T> | a constructor | getConstructor / getDeclaredConstructor(s) |
Parameter | a method/constructor parameter | Executable.getParameters() |
Modifier | a helper for the int modifier bitset | static methods |
Field, Method, and Constructor share a common superclass hierarchy: Member (the interface) and AccessibleObject (which carries setAccessible). That shared base is why "make it accessible" and "read its annotations" look identical no matter which member you hold.
The get… vs getDeclared… split
Almost every lookup method comes in two flavours, and the distinction matters in every later chapter:
getField/getMethod/getConstructor— returns public members, including inherited ones from superclasses and interfaces.getDeclaredField/getDeclaredMethod/getDeclaredConstructor— returns members of any access level (private,protected, package), but only those declared on this exact class — nothing inherited.
So getMethods() sees a public method inherited from a parent but not a private helper on the class itself; getDeclaredMethods() sees the private helper but not the inherited public method. To reach a private inherited member you walk up getSuperclass() calling getDeclared… at each level.
The cost: speed, safety, and encapsulation
Reflection is powerful, and each power has a price.
- Performance. Reflective calls are slower than direct calls — method/field lookups, access checks, and argument boxing all add overhead. The JIT optimizes hot reflective calls well, but reflection in a tight loop is a smell. Cache the
Method/Fieldobjects; never look them up per call. - No compile-time safety. A typo in a method name compiles fine and fails at runtime with
NoSuchMethodException. Refactoring tools renamegetNameeverywhere — except inside your string"getName". - Breaking encapsulation.
setAccessible(true)lets you read and writeprivatestate. That's how serializers populate fields with no setter, but it couples you to internals that the class's author never promised to keep stable. - Module restrictions. Since Java 9, the module system can deny reflective access to non-exported packages. Calling
setAccessible(true)across a module boundary that hasn'topensthe package throwsInaccessibleObjectException.
A worked example: a tiny generic object dumper
To make the scope concrete, here's a single reflective routine that prints any object's fields and their values — the kind of thing a debugger or a logging library does. It names no application type; it works on whatever it's handed.
What to take from the run:
- The
dumpmethod named no concrete type and yet printed bothPointandUser. Its only contract is "give me anObject"; everything about the structure — field names, types, values — came fromgetClass()at runtime. That is the defining move of reflection: one routine, arbitrary inputs. getDeclaredFields()returned all fields including theprivateones, but reading them requiredsetAccessible(true)first. Without that call,f.get(obj)on a private field throwsIllegalAccessException. The lookup and the access are two separate gates.Modifier.toString(f.getModifiers())turned the raw modifier bitset into readable text likeprivate final. Modifiers are stored as anintof flag bits; theModifierhelper decodes them so you don't test bits by hand.- The third object was built with no
new User(...)anywhere in source —Class.forName("…$User")resolved the nested class from a string (note the$separator for nested types), andgetDeclaredConstructor(...).newInstance(...)constructed it. This is the plugin-loading pattern in miniature: a name in, an object out. - Reading the field value (
f.get(obj)) and reading the field metadata (f.getName(),f.getModifiers()) are independent. Metadata needs no instance and no accessibility; values need the object and, for private fields, the accessibility flag.
How the rest of this part is organised
Each remaining chapter zooms into one corner:
- Class objects — the three ways to get a
Class<T>and what it tells you. - Fields — inspecting, reading, and writing fields (including
privateandfinal). - Methods — finding and invoking methods, overload resolution, return values.
- Constructors — building instances reflectively, including private constructors.
- Annotations — reading the metadata you attached in Part 16, at runtime.
- Dynamic proxies — synthesising whole interface implementations at runtime.
Throughout, keep the trade-off in mind: reflection is the right tool when the type genuinely isn't known until runtime, and the wrong tool when it is. The next chapter starts at the root of it all — the Class object.
Practice
A logging library needs to print the field values of any object passed to its 'log(Object o)' method, including objects whose classes it was never compiled against and whose fields are 'private'. Which combination is the minimal correct approach?