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 object
When the JVM loads a class, it creates a unique class for that type. Class object.Class Objects hold structural information such as class names, fields, methods, constructors, and annotations, and are entrances to using reflection.
obtain Class object
by Class.forName() obtain
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, use the 类型.class The most direct and will not throw ClassNotFoundException。
Get through objects
Person person = new Person();
Class<?> personClass = person.getClass();When you already hold an object but need to get its runtime type, you can call the getClass()。
public static void printType(Object object) {
Class<?> objectClass = object.getClass();
System.out.println(objectClass.getName());
}Class Common methods for classes
| method | role |
|---|---|
getName() | Get the complete class name, including package name |
getSimpleName() | Gets the class name that does not contain the package name |
getDeclaredField(String name) | Gets the field with the specified name in the current class |
getDeclaredFields() | Gets all fields declared by the current class |
getDeclaredMethod(String name, Class<?>... parameterTypes) | Gets the method based on the method name and parameter type |
getDeclaredMethods() | Gets all methods declared by the current class |
getDeclaredConstructor(Class<?>... parameterTypes) | Gets the constructor based on the parameter type |
getDeclaredConstructors() | Gets all constructors declared by the current class |
Class.newInstance() It’s outdated. When creating an object, you should first obtain the constructor and then call the constructor’s newInstance()。
Object object = personClass.getDeclaredConstructor().newInstance();use Field operation field
each Field An object represents a field declared in a class. by Field You can obtain the field name, field type, and read or assign values to the fields of the specified object.
Common methods are as follows:
| method | role |
|---|---|
getName() | Get field name |
getType() | Get field type |
setAccessible(true) | Allow reflection access to non-public fields |
set(Object object, Object value) | Assign values to the fields of the specified object |
get(Object object) | Reads the field value of the specified object |
the following example creates a Student Object and modify it through reflection name Fields:
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() Fields declared by the current class, including private fields, can be obtained, but will not be automatically looked up to the parent class. Calling the setAccessible(true)。
use Method calling method
each Method An object represents a method in a class. call invoke() When, 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 are wrapped as InvocationTargetException。
For static methods, call invoke() When the first parameter can be passed in null。
method.invoke(null, "参数");use Constructor create objects
each Constructor An object represents a constructor in a class. You can get the specified constructor through the parameter type, and then call the newInstance() Create objects.
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.
- on
ReflectiveOperationExceptionConduct unified processing. - In modular Java projects, access to non-public members may also be restricted by module access rules.
If you enjoyed this, leave a comment~