Reflection allows inspection and modification of classes, methods, fields, and constructors at runtime — even private members. It powers frameworks like Spring, Hibernate, and JUnit.

Getting Class Objects

  // From an object
String s = "hello";
Class<?> clazz1 = s.getClass();

// From class name
Class<?> clazz2 = Class.forName("java.util.ArrayList");

// From literal
Class<?> clazz3 = String.class;
Class<?> clazz4 = int.class;     // primitive
Class<?> clazz5 = int[].class;   // array
  

Inspecting Class Information

  Class<?> clazz = Class.forName("java.util.ArrayList");

System.out.println(clazz.getName());           // java.util.ArrayList
System.out.println(clazz.getSimpleName());     // ArrayList
System.out.println(clazz.isInterface());       // false
System.out.println(clazz.getSuperclass());     // class java.util.AbstractList
System.out.println(clazz.getPackageName());    // java.util

Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> iface : interfaces) {
    System.out.println("Implements: " + iface.getName());
}
  

Fields

  public class Person {
    private String name;
    public int age;
}

Class<?> clazz = Person.class;

// Public fields only
for (Field field : clazz.getFields()) {
    System.out.println(field.getName());
}

// All declared fields (including private)
for (Field field : clazz.getDeclaredFields()) {
    System.out.println(field.getName() + " : " + field.getType());
}

// Access private field
Person person = new Person();
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(person, "Alice");
System.out.println(nameField.get(person)); // Alice
  

Methods

  Class<?> clazz = String.class;

// Public methods (including inherited)
for (Method method : clazz.getMethods()) {
    System.out.println(method.getName());
}

// Declared methods only
for (Method method : clazz.getDeclaredMethods()) {
    System.out.println(method.getName() + " → " + method.getReturnType());
}

// Invoke method
Method lengthMethod = clazz.getMethod("length");
int len = (int) lengthMethod.invoke("hello"); // 5

Method substringMethod = clazz.getMethod("substring", int.class, int.class);
String sub = (String) substringMethod.invoke("hello", 1, 4); // ell
  

Constructors

  Class<?> clazz = ArrayList.class;

Constructor<?> defaultCtor = clazz.getConstructor();
ArrayList<?> list = (ArrayList<?>) defaultCtor.newInstance();

Constructor<?> sizedCtor = clazz.getConstructor(int.class);
ArrayList<?> list2 = (ArrayList<?>) sizedCtor.newInstance(100);
  

Creating Objects via Reflection

  Class<?> clazz = Class.forName("java.util.HashMap");
Object map = clazz.getDeclaredConstructor().newInstance();
  

Annotations via Reflection

  for (Method method : clazz.getDeclaredMethods()) {
    if (method.isAnnotationPresent(Deprecated.class)) {
        System.out.println(method.getName() + " is deprecated");
    }
}
  

setAccessible and Module System

Since Java 9, accessing private members across modules requires opens:

  Field field = clazz.getDeclaredField("secret");
field.setAccessible(true); // may throw InaccessibleObjectException on Java 17+
  

For Java 17+, add JVM flag for deep reflection:

  java --add-opens java.base/java.lang=ALL-UNNAMED MyApp
  

Performance Considerations

Reflection is slower than direct calls:

  • Method lookup on every call (cache Method objects)
  • Boxing/unboxing overhead
  • Cannot be JIT-optimized as aggressively
  // Cache Method objects
private static final Method STRING_LENGTH;
static {
    STRING_LENGTH = String.class.getMethod("length");
}
  

Best Practices

  • Use reflection only when necessary — frameworks, testing, generic utilities
  • Cache Class, Method, and Field objects — do not look up repeatedly
  • Prefer public APIs over breaking encapsulation with setAccessible
  • Be aware of Java module system restrictions in Java 17+
  • Consider alternatives: interfaces, annotations processors, or code generation