Memra

Reflection basics: Class<?>, getDeclaredFields, and reading annotations at runtime

Getting Class objects, inspecting fields and methods, and how frameworks use reflection under the hood.

What reflection is

Reflection is the ability of a program to examine and modify its own structure at runtime. You can discover the fields, methods, constructors, and annotations of a class without knowing them at compile time. This is how frameworks like Spring (dependency injection), JUnit (test discovery), and Jackson (JSON serialization) work.

Getting a Class object

Three ways to obtain a Class<?> object:

// 1 — from a live object
String s = "hello";
Class<?> c1 = s.getClass();          // java.lang.String

// 2 — from a class literal (compile-time — preferred when you know the type)
Class<String> c2 = String.class;

// 3 — from a name string (runtime lookup, throws ClassNotFoundException)
Class<?> c3 = Class.forName("java.lang.String");

Inspecting fields and methods

Class<?> cls = Employee.class;

// getDeclaredFields — fields declared in THIS class (incl. private), no inherited
for (Field f : cls.getDeclaredFields()) {
    System.out.println(f.getName() + " : " + f.getType().getSimpleName());
}

// getMethods — ALL public methods including inherited ones from superclasses
for (Method m : cls.getMethods()) {
    System.out.println(m.getName());
}

Key distinction: getDeclaredFields() returns this class's own fields including private, but excludes inherited. getFields() returns only public fields but includes inherited ones.

Reading annotations at runtime

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Audited { String by() default "system"; }

// ---

Method m = MyService.class.getMethod("processOrder");

if (m.isAnnotationPresent(Audited.class)) {
    Audited a = m.getAnnotation(Audited.class);
    System.out.println("Audited by: " + a.by());
}

isAnnotationPresent(Class) returns true only if the annotation has RUNTIME retention. getAnnotation(Class) returns the annotation instance, or null if absent.

How frameworks use this

- Spring: scans classes for @Component, @Autowired, @RequestMapping at startup — all require RUNTIME retention. - JUnit 5: discovers @Test methods on test classes by reflecting on all methods and checking isAnnotationPresent(Test.class). - Jackson: reads @JsonProperty on fields and methods to map between JSON keys and Java field names.

Reflection is powerful but slow — it bypasses compile-time type checking. Production code calls reflected methods infrequently; frameworks usually build an optimised call plan at startup and cache it.

NORMAL ~/memra/learn/java-from-zero/reflection-basics utf-8 LF