﻿---
title: Reflection In Java
date: 2024-03-25
excerpt: How Java reflection works, how to obtain Class objects, create instances, invoke methods, and read annotations at runtime, plus its performance trade-offs.
tags: [Java, Reflection]
thumbnail: https://assets.vluv.space/cover/Dev/Java/Java_Reflection.webp
cover: https://assets.vluv.space/cover/Dev/Java/Java_Reflection.webp
updated: 2026-07-08 21:17:39
lang: en
i18n:
  cn: /java_reflection
  translation: 2
---

<script type="module" src="/js/components/tab.js"></script>

## Reflection

**Reflection** lets a program inspect and modify its own structure and behavior at **runtime**. With reflection, a program can dynamically **obtain** detailed information about a class, including its fields, methods, and constructors, and **invoke** these members at runtime.

This capability gives Java some of the flavor of a dynamic language. Frameworks like Spring/Spring Boot and MyBatis, as well as Java annotations, rely heavily on reflection. Also, when you type a `.` after an object or class in an IDE, the IDE lists all available properties and methods of that object or class. That feature is powered by reflection as well.

The `java.lang.reflect` package provides the reflection API. With reflection, you can do the following at runtime:

- Determine the class of any object
- Construct an object of any class
- Determine the fields and methods a class has
- Obtain generic type information
- Access the fields and invoke the methods of any object
- Process annotations
- Generate dynamic proxies

Core reflection APIs:

- `java.lang.Class`: represents a class
- `java.lang.reflect.Method`: represents a method of a class
- `java.lang.reflect.Field`: represents a field of a class
- `java.lang.reflect.Constructor`: represents a constructor of a class

### Runtime Class

`java.lang.Class<T>` represents classes and interfaces in a running Java application.

When a Java program starts, the class loader loads the bytecode from .class files into memory.
During loading, the JVM creates an instance of `java.lang.Class` for each class in use to represent that class; this is commonly called the runtime class.

> [!question] When does class initialization happen?
>
> - Active references to a class (always trigger initialization)
>   - When the JVM starts, the class containing the main method is initialized first;
>   - Creating an object of the class with new;
>   - Accessing the class's static members (except final constants) or static methods;
>   - Making reflective calls on the class via the java.lang.reflect package;
>   - When initializing a class, if its superclass has not been initialized, the superclass is initialized first;
> - Passive references to a class (do not trigger initialization)
>   - When accessing a static field, only the class that actually declares the field is initialized;
>   - Referencing a superclass's static variable through a subclass does not initialize the subclass;
>   - Defining an array of a class type does not trigger that class's initialization;
>   - Referencing a constant does not trigger initialization (constants are placed into the calling class's constant pool during the linking phase)

```java
// 4 ways to obtain a Class instance
public void getClazz() throws ClassNotFoundException {
    // Way 1: Employee.class
    Class c1 = Employee.class;
    // Way 2: emploee.getClass()
    Employee p1 = new Employee();
    Class c2 = p1.getClass();
    // Way 3: Class.forName(String classPath); pass the fully qualified class name
    Class c3 = Class.forName("reflection.Employee");
    // Way 4: use a class loader: ClassLoader (just for reference)
    ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    Class c4 = classLoader.loadClass("reflection.Employee");
    System.out.println(c4); // class reflection.Employee
    System.out.println(c1 == c4); // true
}
```

### Syntax

#### Getting a constructor via reflection and creating an instance

```java
public class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Class<?> personClass = Class.forName("Person");
Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
Object personInstance0 = constructor.newInstance("Alice", 30);
Object personInstance1 = constructor.newInstance(new Object[]{"Alice", 30});
```

### Usage Example

<x-tabs>

<x-tab title="Main.java" active>

```java
package reflection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        // Returns the Class object associated with the class or interface with the
        // given string name.
        Class<?> clazz = Class.forName("reflection.Employee");

        printConstructors(clazz);
        printMethods(clazz);
        printClasses(clazz);
        printAnnotations(clazz);
    }

    private static void printConstructors(Class<?> clazz) {
        System.out.println("======== Constructor API ========");

        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("Constructor Name: " + constructor.getName());
            System.out.println("Constructor Parameter Count: " + constructor.getParameterCount());

            Parameter[] parameters = constructor.getParameters();
            for (Parameter parameter : parameters) {
                System.out.println("Constructor's parameter: " + parameter);
            }
        }
        // Create an object via reflection
        System.out.println("New instance using reflection");
        Constructor<?> cons;
        try {
            cons = clazz.getConstructor(int.class,double.class);
            Employee employee = (Employee) cons.newInstance(1, 2.3);
            System.out.println(employee);
        } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }

        System.out.println("======== Constructor API END ========\n");
    }

    private static void printMethods(Class<?> c) {
        System.out.println("======== Method API ========");

        Method[] methods = c.getDeclaredMethods();
        System.out.println("Number of methods: " + methods.length);

        for (Method method : methods) {
            System.out.println("Method name: " + method.getName());
            System.out.println("Method return type: " + method.getReturnType().getName());
            System.out.println("Method parameter count: " + method.getParameterCount());

            Parameter[] parameters = method.getParameters();
            for (Parameter parameter : parameters) {
                System.out.println("Method's Parameter: " + parameter);
            }
        }
        // Invoke method
        System.out.println("Invoke method using reflection");
        try {
            Method method = c.getMethod("getSalary");
            System.out.println("Method return value: " + method.invoke(new Employee()));
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
                | InvocationTargetException e) {
            e.printStackTrace();
        }

        System.out.println("======== Method API END ========\n");
    }

    private static void printClasses(Class<?> c) {
        System.out.println("======== Class API ========");

        Class<?>[] classes = c.getDeclaredClasses();
        for (Class<?> class1 : classes) {
            System.out.println("Class: " + class1.getName());
        }

        System.out.println("======== Class API END ========\n");
    }

    private static void printAnnotations(Class<?> c) {
        System.out.println("======== Annotation API ========");

        // Get annotations on the class
        Annotation[] classAnnotations = c.getDeclaredAnnotations();
        for (Annotation annotation : classAnnotations) {
            System.out.println("Class Annotation: " + annotation);
        }

        // Get annotations on methods
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            Annotation[] methodAnnotations = method.getDeclaredAnnotations();
            for (Annotation annotation : methodAnnotations) {
                System.out.println("Method Annotation: " + annotation);
            }
        }

        // Get annotations on constructors
        Constructor<?>[] constructors = c.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            Annotation[] constructorAnnotations = constructor.getDeclaredAnnotations();
            for (Annotation annotation : constructorAnnotations) {
                System.out.println("Constructor Annotation: " + annotation);
            }
        }

        System.out.println("======== Annotation API END ========\n");
    }
}
```

</x-tab>

<x-tab title="Employee.java">

```java
package reflection;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyClassAnnotation {
    String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyMethodAnnotation {
    String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
@interface MyConstructorAnnotation {
    String value();
}

@MyClassAnnotation("This is a class annotation")
public class Employee {
    private int id;
    private double salary;

    public Employee() {
    }

    @MyConstructorAnnotation("This is a constructor annotation")
    public Employee(int eid, double esal) {
        this.id = eid;
        this.salary = esal;
    }

    @MyMethodAnnotation("This is a method annotation")
    public void show() {
        System.out.println("Hello");
    }

    public int getId() {
        return id;
    }

    public double getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", salary=" + salary + "]";
    }

    class Inner {
        public void print() {
            System.out.println("Inner class");
        }
    }
}
```

</x-tab>

</x-tabs>

```shell
$ java -version
openjdk version "22.0.2" 2024-07-16
OpenJDK Runtime Environment (build 22.0.2+9-70)
OpenJDK 64-Bit Server VM (build 22.0.2+9-70, mixed mode, sharing)
$ java ./Main.java
======== Constructor API ========
Constructor Name: reflection.Employee
Constructor Parameter Count: 0
Constructor Name: reflection.Employee
Constructor Parameter Count: 2
Constructor's parameter: int arg0
Constructor's parameter: double arg1
New instance using reflection
Employee [id=1, salary=2.3]
======== Constructor API END ========

======== Method API ========
Number of methods: 4
Method name: toString
Method return type: java.lang.String
Method parameter count: 0
Method name: getId
Method return type: int
Method parameter count: 0
Method name: getSalary
Method return type: double
Method parameter count: 0
Method name: show
Method return type: void
Method parameter count: 0
Invoke method using reflection
Method return value: 0.0
======== Method API END ========

======== Class API ========
Class: reflection.Employee$Inner
======== Class API END ========

======== Annotation API ========
Class Annotation: @reflection.MyClassAnnotation("This is a class annotation")
Method Annotation: @reflection.MyMethodAnnotation("This is a method annotation")
Constructor Annotation: @reflection.MyConstructorAnnotation("This is a constructor annotation")
======== Annotation API END ========
```

## Interview Notes

**Pros and cons of reflection**

Pros:

- Obtain class instances dynamically at runtime, which improves flexibility;
- Combines well with dynamic compilation

Cons:

- Reflection is slow: it needs to parse bytecode and resolve objects in memory. Mitigations:
  - Call setAccessible(true) to skip the JDK security check and speed up reflective access;
  - When creating instances of a class repeatedly, caching helps a lot
  - Use ReflectASM, which speeds up reflection via bytecode generation
- Relatively unsafe: it breaks encapsulation (reflection can access private methods and fields)
