Skip to content

Java Reflection

Reflection is a powerful advanced feature provided by Java that allows a running Java program to examine or "reflect upon" itself, and manipulate classes, interfaces, fields, and methods at runtime.

What is Reflection?

The reflection mechanism allows a program at runtime to:

  • Get the complete structure of any class (including its parent class, interfaces, constructors, methods, fields, etc.).
  • Create instances of any class at runtime.
  • Invoke methods of any object at runtime.
  • Get or set field values of any object at runtime, even private members.

The core of reflection is the java.lang.Class class and a series of classes in the java.lang.reflect package, such as Constructor, Method, Field, etc.

The Class Object

For every class loaded into the JVM, a corresponding Class object is created in memory. This Class object contains all information about that class and is the entry point for reflection.

Three Ways to Get a Class Object

  1. Using .class on the class name: The most direct and safest way, checked at compile time.

    java
    Class<String> stringClass = String.class;
  2. Using the getClass() method on an object: If you already have an object instance, you can call its getClass() method.

    java
    String s = "Hello";
    Class<?> sClass = s.getClass();
  3. Using the fully qualified name with Class.forName(): This is the most flexible way, allowing dynamic class loading at runtime based on a string.

    java
    try {
        Class<?> driverClass = Class.forName("com.mysql.cj.jdbc.Driver");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

Using Reflection to Inspect Class Information

Once you have a Class object, you can use it to explore the internal structure of the class.

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

public class ReflectionInfoExample {
    public static void main(String[] args) {
        Class<String> clazz = String.class;

        // Get class name
        System.out.println("Class name: " + clazz.getName());
        System.out.println("Simple class name: " + clazz.getSimpleName());

        // Get all public fields
        System.out.println("\nPublic Fields:");
        for (Field field : clazz.getFields()) {
            System.out.println(" - " + field.getName());
        }

        // Get all declared fields (including private)
        System.out.println("\nDeclared Fields:");
        for (Field field : clazz.getDeclaredFields()) {
            System.out.println(" - " + field.getName());
        }

        // Get all public methods
        System.out.println("\nPublic Methods:");
        for (Method method : clazz.getMethods()) {
            System.out.println(" - " + method.getName());
        }
    }
}

Using Reflection to Create Instances and Invoke Methods

The most powerful feature of reflection is dynamically creating objects and executing operations at runtime.

Creating Instances

  • If the class has a public no-argument constructor, you can directly call clazz.newInstance() (deprecated) or clazz.getDeclaredConstructor().newInstance().
  • If you need to use a parameterized constructor, you first need to get the Constructor object.

Invoking Methods

  • Get the Method object using getMethod() or getDeclaredMethod().
  • Call method.invoke(object, args...) to execute the method.
java
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectionActionExample {
    public static void main(String[] args) throws Exception {
        // 1. Get Class object
        Class<?> clazz = String.class;

        // 2. Create instance
        // Get String(char[]) constructor
        Constructor<?> constructor = clazz.getConstructor(char[].class);
        char[] value = {'H', 'e', 'l', 'l', 'o'}; 
        Object instance = constructor.newInstance(value);
        System.out.println("Created instance: " + instance); // "Hello"

        // 3. Invoke method
        // Get toUpperCase() method
        Method method = clazz.getMethod("toUpperCase");
        // Invoke method, for instance methods, the first parameter is the instance object
        Object result = method.invoke(instance);
        System.out.println("toUpperCase() result: " + result); // "HELLO"
    }
}

Accessing Private Members

Reflection can even bypass private access restrictions. This is useful in unit testing or certain frameworks, but should be used cautiously as it breaks encapsulation.

  • Use getDeclaredField() or getDeclaredMethod() to get private members.
  • Before accessing, you must call setAccessible(true) to disable access security checks.
java
// Assume there's a Person class
// public class Person {
//     private String name;
//     public Person(String name) { this.name = name; }
// }

// Person person = new Person("Alice");
// Field nameField = person.getClass().getDeclaredField("name");
// nameField.setAccessible(true); // Disable access checking
// String nameValue = (String) nameField.get(person);
// System.out.println(nameValue); // Output "Alice"

Pros and Cons of Reflection

Pros:

  • Flexibility and dynamism: Enables programs to load, inspect, and use classes unknown at compile time at runtime.
  • Framework development: Is the cornerstone of many modern frameworks (such as Spring, Hibernate, JUnit) for implementing dependency injection, ORM, and other features.

Cons:

  • Performance overhead: Reflection operations are much slower than direct code calls.
  • Security issues: Can bypass access control and break encapsulation.
  • Poor code readability: Reflection code is typically more complex and harder to understand and debug.

Content is for learning and research only.