Exploring Java Reflection API and Class Introspection

Understanding Member Introspection

The Java Reflection API provides a powerful mechanism to inspect classes at runtime. It distinguishes between accessing public members (including those inherited from superclasses) and accessing all declared members (restricted to the current class regardless of access modifiers).

  • Public Members (Inherited): Methods like getFields(), getMethods(), and getConstructors() return public elements from the current class and its superclasses. Note that constructors are not inherited.
  • Declared Members (Local): Methods like getDeclaredFields(), getDeclaredMethods(), and getDeclaredConstructors() return all members (private, protected, package-private, and public) defined specifically with in the class, excluding inherited members.

Domain Model Setup

To demonstrate these features, we define a base class User and a subclass Administrator.

class User {
    public User() {}
    public User(String identity) {}
    private String secretToken = "token123";
    protected int internalId = 100;
    public String username = "admin";

    private void logPrivateAction() {
        System.out.println("Executing private action");
    }
    protected void logProtectedAction() {
        System.out.println("Executing protected action");
    }
    public void logPublicAction() {
        System.out.println("Executing public action");
    }
}

class Administrator extends User {
    public Administrator() {}
    public Administrator(String role) { super(role); }
    private String adminKey = "root_key";
    protected int accessLevel = 5;
    public String department = "IT";

    private void performAudit() {
        System.out.println("Executing audit");
    }
    private void performAudit(String detail) {
        System.out.println("Executing audit: " + detail);
    }
    protected void updateConfig() {
        System.out.println("Updating configuration");
    }
    public void sendReport() {
        System.out.println("Sending report");
    }
    public void sendReport(String recipient) {
        System.out.println("Sending report to " + recipient);
    }
}

Analyzing Class Structure

The following code demonstrates how to retrieve and compare metadata. We utilize the Modifier class to interpret access flags.

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectionAnalyzer {
    public static void main(String[] args) throws Exception {
        Class> targetClass = Class.forName("com.example.demo.Administrator");

        System.out.println("--- Public Fields (Inherited + Local) ---");
        Field[] publicFields = targetClass.getFields();
        for (Field f : publicFields) {
            System.out.println(Modifier.toString(f.getModifiers()) + " " + f.getName());
        }

        System.out.println("\n--- Public Methods (Inherited + Local) ---");
        Method[] publicMethods = targetClass.getMethods();
        for (Method m : publicMethods) {
            System.out.println(Modifier.toString(m.getModifiers()) + " " + m.getName());
        }

        System.out.println("\n--- Declared Fields (Local Only) ---");
        Field[] declaredFields = targetClass.getDeclaredFields();
        for (Field f : declaredFields) {
            System.out.println(Modifier.toString(f.getModifiers()) + " " + f.getName());
        }

        System.out.println("\n--- Declared Methods (Local Only) ---");
        Method[] declaredMethods = targetClass.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println(Modifier.toString(m.getModifiers()) + " " + m.getName());
        }
    }
}

Accessing Private Fields

Reflectino allows access to private fields by suppressing Java's access control checks via setAccessible(true). The example below retrieves field values directly by name.

Administrator sysAdmin = new Administrator();
Class> metaClass = sysAdmin.getClass();

// Retrieving fields by specific names
Field keyField = metaClass.getDeclaredField("adminKey");
Field levelField = metaClass.getDeclaredField("accessLevel");
Field deptField = metaClass.getDeclaredField("department");

// Bypassing encapsulation for private members
keyField.setAccessible(true);

System.out.println("Private Key: " + keyField.get(sysAdmin));
System.out.println("Protected Level: " + levelField.get(sysAdmin));
System.out.println("Public Dept: " + deptField.get(sysAdmin));

Invoking Methods Dynamically

Methods can be invoked dynamically using the invoke method. For private methods, accessibility must be enabled. Overloaded methods are distinguished by their parameter types.

Administrator sysAdmin = new Administrator();
Class> metaClass = sysAdmin.getClass();

// Retrieving methods with specific parameter signatures
Method audit = metaClass.getDeclaredMethod("performAudit", String.class);
Method report = metaClass.getMethod("sendReport", String.class);

// Enable access to private method
audit.setAccessible(true);

// Invoke methods on the instance
audit.invoke(sysAdmin, "System Security Check");
report.invoke(sysAdmin, "Engineering Team");

Generic Array Resizing

Reflection can be used to implement generic array operations, such as resizing an array of an unknown type at runtime. The Array class provides utilities for dynamic array creation and manipulation.

import java.lang.reflect.Array;

public static Object resizeArray(Object source, int newSize) {
    Class> componentType = source.getClass().getComponentType();
    Object newArray = Array.newInstance(componentType, newSize);
    
    int elementsToCopy = Math.min(Array.getLength(source), newSize);
    System.arraycopy(source, 0, newArray, 0, elementsToCopy);
    
    return newArray;
}

public static void main(String[] args) {
    User[] originalUsers = { new User(), new User() };
    User[] expandedUsers = (User[]) resizeArray(originalUsers, 10);
    System.out.println("New array length: " + expandedUsers.length);
}

Tags: java reflection introspection classmetadata runtime

Posted on Wed, 07 Oct 2026 16:22:54 +0000 by choubix