Recently, the knowledge is quite fragmented and not suitable for organizing into an article, so here is an old study note on reflection, with the main content as follows:
- Reflection Mechanism
- Reflection to Obtain Class Information
- Reflection to Operate on Class Information
- Reflection to Obtain Generics
- Reflection to Obtain Annotation Information
Reflection Mechanism#
The reflection mechanism in Java refers to the ability to know all the properties and methods of any class at runtime. Reflection is a mechanism that allows dynamic retrieval of class information while the code is running, enabling access to information that cannot be obtained at compile time. When any class is first loaded by a ClassLoader, a corresponding Class object is automatically generated, which holds all the information about that class. This ability to dynamically obtain information about the Class object and to operate on the properties and methods of the Class object after it has been loaded is known as Java's reflection mechanism.
The key to Java's reflection mechanism is obtaining the Class object of a specific class. The following content explains how to obtain and dynamically operate on class-related information through this Class object. To facilitate the use of reflection in the following sections, a User class is created for use, as detailed below:
package com.manu.reflection.bean;
/**
* Reflection Test Class
*/
public class User {
private int id;
private String name;
private String password;
public User() {
super();
}
public User(int id, String name, String password) {
super();
this.id = id;
this.name = name;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", password=" + password + "]";
}
}
Reflection to Obtain Class Information#
Here is a summary of how to obtain basic information about a class, such as its constructors, properties, methods, etc. This can be done through the getter methods corresponding to the Class object of a certain class to obtain the class name, constructors, properties, methods, etc. The following example illustrates how to obtain a class's constructors:
clazz.getConstructors()
: This retrieves all public constructors of a class, similar togetFields()
andgetMethods()
, which also include public properties and methods inherited from the parent class.clazz.getDeclaredConstructors()
: This retrieves all declared constructors of a class, limited to the class itself.
Additionally, specific constructors, properties, methods, etc., can be obtained. The following code mainly illustrates how to obtain declared constructors, properties, and methods:
/**
* Reflection to Obtain Class Information
*/
private static void getReflectClassInfo() {
try {
// Obtain the Class object of a certain class
String name = "com.manu.reflection.bean.User";
Class clazz = Class.forName(name);
// Reflectively obtain the class name
System.out.println("----------Reflectively Obtain Class Name----------");
String n1 = clazz.getName(); // Full path: package name + class name (com.manu.reflection.bean.User)
String n2 = clazz.getSimpleName(); // Class name (User)
System.out.println("Obtained class name n1: " + n1);
System.out.println("Obtained class name n2: " + n2);
// Reflectively obtain the class constructors
System.out.println("----------Reflectively Obtain Class Constructors----------");
Constructor<User> c1 = clazz.getDeclaredConstructor(null);
System.out.println("Obtained no-argument constructor: " + c1);
Constructor<User> c2 = clazz.getDeclaredConstructor(int.class, String.class, String.class);
System.out.println("Obtained parameterized constructor: " + c2);
Constructor[] constructors = clazz.getDeclaredConstructors();
for(Constructor c: constructors) {
System.out.println("Obtained all constructors: " + c);
}
// Reflectively obtain the class properties
System.out.println("----------Reflectively Obtain Class Properties----------");
Field f1 = clazz.getDeclaredField("name");
System.out.println("Obtained property named 'name': " + f1);
Field[] fields = clazz.getDeclaredFields();
for(Field f : fields) {
System.out.println("Obtained all properties: " + f);
}
// Reflectively obtain the class methods
System.out.println("----------Reflectively Obtain Class Methods----------");
Method m1 = clazz.getDeclaredMethod("getName", null); // Obtain no-argument method
Method m2 = clazz.getDeclaredMethod("setName", String.class); // Obtain parameterized method
System.out.println("Obtained method named 'getName': " + m1);
System.out.println("Obtained method named 'setName': " + m2);
Method[] methods = clazz.getDeclaredMethods();
for(Method m: methods) {
System.out.println("Obtained all methods: " + m);
}
} catch (Exception e) {
e.printStackTrace();
}
}
The execution results of the above code are as follows:
----------Reflectively Obtain Class Name----------
Obtained class name n1: com.manu.reflection.bean.User
Obtained class name n2: User
----------Reflectively Obtain Class Constructors----------
Obtained no-argument constructor: public com.manu.reflection.bean.User()
Obtained parameterized constructor: public com.manu.reflection.bean.User(int, java.lang.String, java.lang.String)
Obtained all constructors: public com.manu.reflection.bean.User()
Obtained all constructors: public com.manu.reflection.bean.User(int, java.lang.String, java.lang.String)
----------Reflectively Obtain Class Properties----------
Obtained property named 'name': private java.lang.String com.manu.reflection.bean.User.name
Obtained all properties: private int com.manu.reflection.bean.User.id
Obtained all properties: private java.lang.String com.manu.reflection.bean.User.name
Obtained all properties: private java.lang.String com.manu.reflection.bean.User.password
----------Reflectively Obtain Class Methods----------
Obtained method named 'getName': public java.lang.String com.manu.reflection.bean.User.getName()
Obtained method named 'setName': public void com.manu.reflection.bean.User.setName(java.lang.String)
Obtained all methods: public java.lang.String com.manu.reflection.bean.User.toString()
Obtained all methods: public java.lang.String com.manu.reflection.bean.User.getName()
Obtained all methods: public int com.manu.reflection.bean.User.getId()
Obtained all methods: public void com.manu.reflection.bean.User.setName(java.lang.String)
Obtained all methods: public java.lang.String com.manu.reflection.bean.User.getPassword()
Obtained all methods: public void com.manu.reflection.bean.User.setId(int)
Obtained all methods: public void com.manu.reflection.bean.User.setPassword(java.lang.String)
Reflection to Operate on Class Information#
Using Java's reflection mechanism, you can obtain a class's constructors, properties, and methods, and then perform related operations on that class. The following illustrates how to construct an object of that class, operate on its properties, and invoke its methods using Java's reflection mechanism:
/**
* Reflection to Operate on Class Information
*/
private static void setReflectClassInfo() {
try {
// Obtain the Class object of a certain class
String name = "com.manu.reflection.bean.User";
Class clazz = Class.forName(name);
// Reflectively operate on the class constructors
System.out.println("----------Reflectively Operate on Class Constructors----------");
Constructor<User> c1 = clazz.getDeclaredConstructor(null); // Obtain no-argument constructor
Constructor<User> c2 = clazz.getDeclaredConstructor(int.class, String.class, String.class); // Obtain parameterized constructor
User u1 = c1.newInstance();
User u2 = c2.newInstance(1000, "jzman-blog", "111111");
System.out.println("u1: " + u1);
System.out.println("u2: " + u2);
// Reflectively operate on the class properties
System.out.println("----------Reflectively Operate on Class Properties----------");
User u3 = c1.newInstance();
Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // Set this property to be accessible without security checks
field.set(u3, "jzman"); // Reflectively set the name property of User object
System.out.println("u3: " + u3);
System.out.println("Obtained User object u3's name property value: " + field.get(u3));
// Reflectively operate on the class methods
System.out.println("----------Reflectively Operate on Class Methods----------");
User u4 = c1.newInstance();
Method method = clazz.getDeclaredMethod("setPassword", String.class);
method.invoke(u4, "222222"); // Set the password property of User object u4
System.out.println("u4: " + u4);
} catch (Exception e) {
e.printStackTrace();
}
}
The execution results of the above code are as follows:
----------Reflectively Operate on Class Constructors----------
u1: User [id=0, name=null, password=null]
u2: User [id=1000, name=jzman-blog, password=111111]
----------Reflectively Operate on Class Properties----------
u3: User [id=0, name=jzman, password=null]
Obtained User object u3's name property value: jzman
----------Reflectively Operate on Class Methods----------
u4: User [id=0, name=null, password=222222]
In actual development, you will definitely encounter situations where a component requires a certain property, but that property is private. In such cases, Java's reflection mechanism can be used.
Reflection to Operate on Generics#
Java uses a mechanism called type erasure for generics, meaning that generics in Java are only used by the compiler (javac) to ensure data safety and eliminate the hassle of forced type conversion. After compilation, all types related to generics will be erased. To operate on these types using reflection, four new types have been introduced: GenericArrayType
, ParameterizedType
, TypeVariable<T>
, and WildcardType
, which represent types that cannot be classified into the Class class but are equivalent to raw types. In other words, the types that can normally obtain the corresponding Class object are still Class objects, such as primitive data types.
Operating on generics through reflection involves Java's Type
. In Java, Type
is the common interface for all types, including raw types, parameterized types, array types, type variables, and basic types, declared as follows:
/**
* Type is the common interface for all types in the Java language
* These types include raw types, parameterized types, array types, type variables, and basic types
*/
public interface Type {
default String getTypeName() {
return toString();
}
}
Type
has four direct subinterfaces, with specific meanings as follows:
GenericArrayType: Represents a generic array type, such as ArrayList<String>[] listArray
ParameterizedType: Represents a parameterized type (generic), such as ArrayList<String> list
TypeVariable<T>: Represents a type variable, such as T
WildcardType: Represents a wildcard type, such as ?, ? extends Number, or ? super Integer
The following illustrates how to use reflection to operate on generic types, specifically GenericArrayType
and ParameterizedType
:
// Used to test obtaining generic properties
private String[] array;
private List<String>[] listArray;
// Used to test obtaining generic methods
private String testGenericType(Map<String, Integer> map, String[] array, int name, User user) {
System.out.println("testGenericType");
return null;
}
// Obtain generics through reflection
private static void refectGenericType() {
try {
System.out.println("---------GenericArrayType---------");
// Obtain generic array (GenericArrayType)
Field field = ReflectTest02.class.getDeclaredField("listArray"); // Obtain property listArray
GenericArrayType type = (GenericArrayType) field.getGenericType();
System.out.println("Obtained generic array: " + type);
System.out.println("---------ParameterizedType---------");
// Obtain parameterized type (generic) (ParameterizedType)
Method method = ReflectTest02.class.getDeclaredMethod("testGenericType", Map.class, String[].class, int.class, User.class);
Type[] types = method.getGenericParameterTypes(); // Obtain method parameter types
for(Type type1: types) {
System.out.println("Method parameter type: " + type1);
if(type1 instanceof ParameterizedType) {
System.out.println("ParameterizedType: " + type1);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
The execution results of the method refectGenericType
are as follows:
---------GenericArrayType---------
Obtained generic array: java.util.List<java.lang.String>[]
---------ParameterizedType---------
Method parameter type: java.util.Map<java.lang.String, java.lang.Integer>
ParameterizedType: java.util.Map<java.lang.String, java.lang.Integer>
Method parameter type: class [Ljava.lang.String;
Method parameter type: int
Method parameter type: class com.manu.reflection.bean.User
You can refer to the code for the corresponding output results.
Reflection to Obtain Annotation Information#
Reflection can also be used to obtain annotation information. If you are unfamiliar with annotations, you can refer to a previous article I shared on Java annotations. Here, I will briefly simulate how database table fields correspond to Java object properties through annotation information. This is why, when using some database frameworks, we can create tables through some table annotations and field annotations. This involves using reflection to obtain annotation information to generate SQL, thereby creating the corresponding tables.
Create two annotations, one for table annotations and one for field annotations, as follows:
// Table annotation
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableAnnotation {
String value();
}
// Field annotation
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotation {
String column();
int length();
String type();
}
Then, create a class and use these annotations, as follows:
/**
* Reflection Read Annotation Information Test Bean
* @author jzman
*/
@TableAnnotation(value = "student_table")
public class Student {
@FieldAnnotation(column = "uid", length = 20, type = "int")
private int sId;
@FieldAnnotation(column = "name", length = 10, type = "varchar")
private String sName;
@FieldAnnotation(column = "uiaged", length = 3, type = "varchar")
private int sAge;
// setter, getter methods
//...
}
Finally, obtain the annotation information as follows:
/**
* Reflection to Obtain Annotation Information
* @author jzman
*/
public class ReflectTest03 {
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.manu.reflection.bean.Student");
// Reflectively obtain class annotation information
TableAnnotation tableAnnotation = (TableAnnotation) clazz.getAnnotation(TableAnnotation.class);
System.out.println("Reflectively obtained class annotation information: " + tableAnnotation);
// Reflectively obtain property annotation information
Field field = clazz.getDeclaredField("sName");
FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
System.out.println("Reflectively obtained property annotation information: " + fieldAnnotation);
// The method for obtaining other annotation information is similar
//...
} catch (Exception e) {
e.printStackTrace();
}
}
}
The execution results of the above code are as follows:
Reflectively obtained class annotation information: @com.manu.reflection.TableAnnotation(value=student_table)
Reflectively obtained property annotation information: @com.manu.reflection.FieldAnnotation(column=name, length=10, type=varchar)
Clearly, the corresponding annotation information has been obtained through reflection. These annotations mark some key information about the corresponding database table for the class, allowing for the generation of corresponding SQL statements. This makes it easier to understand the mapping relationship between database table fields and Java object properties.
Using reflection to access private properties or methods requires setting setAccessible
to true to bypass Java's security checks, allowing access to private properties, methods, etc. Additionally, setting setAccessible
to true can improve the performance of reflection to some extent.