Java reflection mechanism

Published 2026-07-29 10:55 Updated 2026-07-29 10:55 1331 words 7 min read ... Page views

This article introduces the core concepts and practical applications of Java reflection mechanism. Reflection allows programs to dynamically obtain class information, create objects, access fields, and call methods at runtime, and is widely used in scenarios such as frameworks, dependency injection, and generic DAOs. The article illustrates with examples how to implement dynamic operations through Class objects, Fields, Methods and Constructors, and points out the implementation ideas and limitations of reflection in the general DAO. At the same time, it emphasizes that security, readability and type verification should be paid attention to when using reflection, so as to avoid directly using user input as class names or field names.

Java reflection mechanism

Overview of reflection

Java reflection refers to the mechanism by which a program obtains structural information of a class during runtime and dynamically creates objects, accesses fields, calls methods, or calls constructors.

Normally, the code is clear at compile time which type to use. Using reflection, a program can decide which class to load at run time based on the class name or configuration file, so reflection is often used in frameworks, dependency injection, object mapping, and common tool classes.

Loading of classes and Class objects

When the JVM loads a class, it creates a unique Class object for that type. The Class object holds structural information such as class names, fields, methods, constructors, and annotations, and is an entry for using reflection.

Get Class objects

Obtained through Class.forName()

try {
    Class<?> personClass = Class.forName("com.hyxy.Person");
    System.out.println(personClass.getName());
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

This method loads the class through the full class name, which is suitable for scenarios where the class name comes from the configuration file or the target type cannot be directly referenced at compile time.

Obtained through class literals

Class<Person> personClass = Person.class;

When the target type is already known at compile time, it is most straightforward to use 类型.class, and ClassNotFoundException will not be thrown.

Get through objects

Person person = new Person();
Class<?> personClass = person.getClass();

getClass() can be called when you already hold an object but need to obtain its runtime type.

public static void printType(Object object) {
    Class<?> objectClass = object.getClass();
    System.out.println(objectClass.getName());
}

Common methods for Class

MethodAction
getName()Get the complete class name, including the package name
getSimpleName()Get the class name
getDeclaredField(String name)Gets the field for the specified name in the current class
getDeclaredFields()Get all fields for the current class declaration
getDeclaredMethod(String name, Class<?>... parameterTypes)Obtaining method based on method name and parameter type
getDeclaredMethods()All ways to obtain current class declarations
getDeclaredConstructor(Class<?>... parameterTypes)Obtain constructor based on parameter type
getDeclaredConstructors()Obtain all constructors for current class declarations

Class.newInstance() is outdated. When creating an object, you should first obtain the constructor and then call the constructor’s newInstance().

Object object = personClass.getDeclaredConstructor().newInstance();

Using Field Operation Fields

Each Field object represents a field declared in the class. Through Field, you can obtain the field name and field type, and read or assign values to the fields of the specified object.

Common methods are as follows:

MethodAction
getName()Get field name
getType()Get Field Type
setAccessible(true)allows reflection access to non-public fields
set(Object object, Object value)Assign
get(Object object)Read the field value of the specified object

The following example creates a Student object and modifies its name field through reflection:

package com.hyxy;

import java.lang.reflect.Field;

public class FieldTest {
    public static void main(String[] args) {
        try {
            Class<?> studentClass = Class.forName("com.hyxy.Student");
            Object student = studentClass.getDeclaredConstructor().newInstance();

            Field nameField = studentClass.getDeclaredField("name");
            nameField.setAccessible(true);
            nameField.set(student, "Tom");

            Object name = nameField.get(student);
            System.out.println(name);
        } catch (ReflectiveOperationException e) {
            e.printStackTrace();
        }
    }
}

getDeclaredField() can obtain the fields declared by the current class, including private fields, but will not automatically look up the parent class. You usually need to call setAccessible(true) when accessing private fields.

Call methods using Method

Each Method object represents a method in the class. When calling invoke(), the first parameter is the object to which the method belongs, and the subsequent parameters are the arguments that need to be passed in to call the method.

package com.hyxy;

import java.lang.reflect.Method;

public class MethodTest {
    public static void main(String[] args) {
        try {
            Class<?> studentClass = Class.forName("com.hyxy.Student");
            Object student = studentClass.getDeclaredConstructor().newInstance();

            Method method1 = studentClass.getDeclaredMethod("study");
            Method method2 = studentClass.getDeclaredMethod(
                    "study",
                    String.class,
                    int.class
            );

            method1.setAccessible(true);
            method2.setAccessible(true);

            method1.invoke(student);
            method2.invoke(student, "Java", 24);
        } catch (ReflectiveOperationException e) {
            e.printStackTrace();
        }
    }
}

When looking for overloaded methods, you must provide not only the method name, but also an accurate list of parameter types. During calling a method, exceptions thrown inside the target method will be wrapped as InvocationTargetException.

For static methods, the first parameter when calling invoke() can be passed to null.

method.invoke(null, "参数");

Creating objects using Constructor

Each Constructor object represents a constructor in the class. You can obtain the specified constructor through the parameter type, and then call newInstance() to create the object.

package com.hyxy;

import java.lang.reflect.Constructor;

public class ConstructorTest {
    public static void main(String[] args) {
        try {
            Class<Student> studentClass = Student.class;
            Constructor<Student> constructor = studentClass.getDeclaredConstructor(
                    String.class,
                    int.class,
                    int.class
            );

            constructor.setAccessible(true);
            Student student = constructor.newInstance("Tom", 20, 110011);

            System.out.println(
                    student.getName() + "," +
                    student.getAge() + "," +
                    student.getNo()
            );
        } catch (ReflectiveOperationException e) {
            e.printStackTrace();
        }
    }
}

Reflection implements universal DAO

basic ideas

Traditional DAOs usually write add, delete, modify, and check codes for each table separately. The universal DAO can read the class name and field information of entity classes through reflection, and then dynamically generate SQL.

A simple agreement is:

  • The entity class name corresponds to the table name.
  • Entity class field names correspond to column names.
  • The primary key field name is passed in by the caller.
  • Self-added primary keys are not passed in as a placeholder parameter when inserted.

In real projects, class names and table names, field names and column names are often not exactly the same, so the mapping is usually completed in conjunction with annotations.

Examples of common new methods

package com.hyxy.dao;

import org.apache.commons.dbutils.QueryRunner;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class CommonDao {
    public int insert(Object entity, String primaryKeyName, boolean autoIncrement) {
        Class<?> entityClass = entity.getClass();
        String tableName = entityClass.getSimpleName();
        Field[] fields = entityClass.getDeclaredFields();

        List<String> columnNames = new ArrayList<>();
        List<String> placeholders = new ArrayList<>();
        List<Object> parameterValues = new ArrayList<>();

        for (Field field : fields) {
            String fieldName = field.getName();

            if (autoIncrement && fieldName.equals(primaryKeyName)) {
                continue;
            }

            field.setAccessible(true);
            columnNames.add(fieldName);
            placeholders.add("?");

            try {
                parameterValues.add(field.get(entity));
            } catch (IllegalAccessException e) {
                throw new RuntimeException("读取字段失败:" + fieldName, e);
            }
        }

        String sql = "insert into " + tableName +
                "(" + String.join(",", columnNames) + ") values(" +
                String.join(",", placeholders) + ")";

        QueryRunner queryRunner = new QueryRunner();

        try (Connection connection = DBUtil.getConnection()) {
            return queryRunner.update(
                    connection,
                    sql,
                    parameterValues.toArray()
            );
        } catch (SQLException e) {
            throw new RuntimeException("执行新增操作失败", e);
        }
    }
}

This example shows the basic combination of reflection and JDBC encapsulation, but still has the following limitations:

  • The entity class name must be consistent with the table name.
  • The field name must be consistent with the column name.
  • Field order relies on reflection return results and should not be used as a fixed database order.
  • Real projects also need to deal with primary keys, inherited fields, static fields, transactions, and exception regimes.
  • Table names and column names cannot use precompiled placeholders, so they must come from trusted mapping information and cannot be directly entered by user.

Considerations for use of reflection

Reflection improves program dynamics, but it also reduces code readability and bypasses some compile-time type checking. The following principles should be followed when using:

  • When it can be called directly, ordinary Java code is preferred.
  • Perform type and parameter verification on the reflection entrance.
  • Don’t treat unverified user input directly as class names, method names, table names, or column names.
  • Carry out unified processing of ReflectiveOperationException.
  • In modular Java projects, access to non-public members may also be restricted by module access rules.

If you enjoyed this, leave a comment~

... Page views
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka