Phase 4 · Advanced JavaModule 26~36 min read

Annotations & Reflection

Add metadata with annotations and inspect or invoke code at runtime with reflection.

What you'll learn

Annotations attach metadata to your code, and reflection lets a program inspect and manipulate itself at runtime. Together they power the frameworks you'll use every day — JUnit, Spring, Hibernate, Jackson. This module reveals the magic behind them.

By the end you'll be able to:

  • Use built-in annotations and create your own
  • Control annotations with @Retention and @Target
  • Inspect classes, methods, and fields with reflection
  • Invoke methods dynamically
  • Understand how frameworks combine the two — and the trade-offs

Built-in annotations

An annotation is metadata — a note on your code that tools, the compiler, or a framework can read. You've already used @Override. The JDK ships a few you'll see constantly:

Builtin.java
class Base {
    void speak() { }
}

class Dog extends Base {
    @Override                      // compiler verifies this overrides
    void speak() { System.out.println("Woof"); }

    @Deprecated                    // warns callers this is obsolete
    void oldTrick() { }

    @SuppressWarnings("unchecked") // silences a specific warning
    void risky() { }
}
  • @Override — the compiler checks you really are overriding a method
  • @Deprecated — marks something obsolete; callers get a warning
  • @SuppressWarnings — silences a specific compiler warning
  • @FunctionalInterface — enforces the single-abstract-method rule (Module 10)

Custom annotations

You can define your own annotations with @interface. Two meta-annotations configure them: @Retention says how long the annotation survives (source only, in the class file, or available at RUNTIME for reflection), and @Target says where it can be applied (methods, fields, classes…):

Custom.java
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)   // keep it available at runtime
@Target(ElementType.METHOD)           // may only annotate methods
@interface Test { }

class MathTests {
    @Test void addsCorrectly()      { /* ... */ }
    @Test void subtractsCorrectly() { /* ... */ }
    void helperNotATest()           { /* ... */ }
}

Note

For an annotation to be readable by reflection at runtime — the way test runners and frameworks use them — it must be @Retention(RetentionPolicy.RUNTIME). The default keeps it only in the class file, invisible to reflection.

Reflection

Reflection (the java.lang.reflect package) lets code examine itself at runtime — list a class's methods and fields, read annotations, even access private members. Every object can hand you its Class object, the gateway to all of this:

Reflection.java
import java.lang.reflect.*;

Class<?> c = "hello".getClass();     // get the runtime type
System.out.println(c.getName());     // java.lang.String

// look up a method by name and CALL it dynamically
Method m = String.class.getMethod("toUpperCase");
Object result = m.invoke("hello");
System.out.println(result);

Dynamic invocation

Reflection can go further and call methods chosen at runtime by name, or create objects, without knowing the types at compile time. This is how a framework runs your annotated methods without ever having seen your class:

Scan.java
import java.lang.reflect.*;

// This is (roughly) how a test runner finds and runs tests:
for (Method m : MathTests.class.getDeclaredMethods()) {
    if (m.isAnnotationPresent(Test.class)) {
        System.out.println("Running test: " + m.getName());
        m.invoke(new MathTests());
    }
}

How frameworks use it

Annotations + reflection is the recipe behind almost every Java framework. You sprinkle annotations on your code; the framework scans for them with reflection and does the heavy lifting:

The framework pattern

You add annotations

@Test, @Autowired, @Entity…

→

Framework scans classes

using reflection at startup

→

It acts on them

runs tests, injects deps, maps tables

Use it sparingly in your own code

Reflection is powerful but comes at a cost: it's slower than direct calls, it bypasses encapsulation (breaking type safety and access rules), and errors surface at runtime instead of compile time. Reach for it to build tools and frameworks — not for everyday logic that a normal method call would handle.

Recap & quick check

Key takeaways

  • Annotations attach metadata; built-ins include @Override, @Deprecated, @SuppressWarnings.
  • Create your own with @interface; configure with @Retention (RUNTIME for reflection) and @Target.
  • Reflection inspects classes, methods, and fields at runtime via the Class object.
  • It can invoke methods and create objects dynamically by name.
  • Frameworks combine annotations + reflection; use reflection sparingly in your own code (slow, unsafe).

Quick check

1. What are annotations?

2. For a custom annotation to be readable by reflection, its retention must be…

3. What does reflection let a program do?

4. How do frameworks like JUnit and Spring work under the hood?

5. A downside of reflection is that it…

Great — you've seen the machinery behind Java's frameworks. Next up: Module 27 — JVM Internals & Memory Management.