1. The Concept of Java Reflection
Java’s reflection mechanism refers to the ability during program runtime to construct objects of any class, understand the class to which any object belongs, understand the member variables and methods of any class, and call the properties and methods of any object. This capability of dynamically obtaining program information and dynamically calling objects is called Java’s reflection mechanism. Reflection is considered key to dynamic languages.
2. Reflection API
(1) Commonly Used Classes in Reflection
java.lang.Class
: Classes in Java can also be considered special objects, and Class represents class objects.
java.lang.reflect.Constructor
: Class constructors.
java.lang.reflect.Field
: Class member variables.
java.lang.reflect.Method
: Class member methods.
(2) Three Methods to Get Class Objects
Now I’ll create a User
class in the top.mygld.demo.pojo
package as follows for demonstration:
package top.mygld.demo.pojo;
public class User {
private String name;
private int age;
}
Then I’ll demonstrate getting class objects in the main class.
i) Get directly through ClassName.class
, for example:
Class c = User.class; //Assuming User is our encapsulated class
ii) Get through Class.forName("full class name")
, for example:
Class c = Class.forName("top.mygld.demo.pojo.User");
iii) Get through specific object.getClass()
, for example:
User user = new User("Xiao Ming",30);
Class c = user.getClass();
The object c
obtained through the above three methods is the class object corresponding to the User
class. Next, we can call some methods in c
to obtain internal information of the User
class and then manipulate the internal information, which is Java reflection.
(3) Basic Information of Class Objects
In step (2), we obtained the class object c
. What would we get if we directly print and output c
? Let’s try it.
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
public class ReflectionDemo {
public static void main(String[] args){
Class c = User.class;
System.out.println(c);
}
}
As shown above, it outputs class + full class name
. This shows that the Class
class overrides the toString
method.
Additionally, the Class
class provides two main member methods: getName
and getSimpleName
, used to get the full class name and simple class name of the class object respectively, with effects as follows.
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
public class ReflectionDemo {
public static void main(String[] args){
Class c = User.class;
System.out.println(c.getName());
System.out.println(c.getSimpleName());
}
}
(4) Getting Constructor Information of Target Class Through Reflection
Through the reflection mechanism, we can obtain constructor information of the target class, such as the number of constructors, constructor parameter situations (no-arg constructor, parameterized constructor), number of parameters in parameterized constructors, parameter types, access modifiers, etc.
To facilitate testing, I’ll rewrite the User
class as follows:
package top.mygld.demo.pojo;
public class User {
private String name;
private int age;
public User(){}
private User(String name){
this.name = name;
}
protected User(int age){
this.age = age;
}
public User(String name, int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Using the getDeclaredConstructors()
method in Class
can get an array of constructor objects for all constructors in the target class. Using getParameterCount()
in Constructor
can get the number of parameters in the corresponding constructor, as follows:
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
import java.lang.reflect.Constructor;
public class ReflectionDemo {
public static void main(String[] args){
Class c = User.class;
Constructor[] cons = c.getDeclaredConstructors();
System.out.println("Total " + cons.length + " constructors") ;
for(Constructor con : cons){
System.out.println(con + " Parameter count: " + con.getParameterCount());
}
}
}
Of course, if you only want to get constructors modified by public
, use the getConstructors()
method in Class
, which won’t be demonstrated here.
So there’s a question: if I want to get a single specified constructor object, how do I get it? Actually, you just need to call the overloaded method getDeclaredConstructor(Class<?>... parameterTypes)
of getDeclaredConstructor
, where parameterTypes
only needs to pass in ClassName.class
or primitive type.class
, as follows:
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
public class ReflectionDemo {
public static void main(String[] args) throws NoSuchMethodException {
Class c = User.class;
System.out.println(c.getDeclaredConstructor(int.class));
System.out.println(c.getDeclaredConstructor(String.class, int.class));
}
}
Of course, if you want to get a constructor modified by public
, you can also use getConstructor(Class<?>... parameterTypes)
in Class
to get it, where its parameter can be empty, indicating getting a no-arg constructor modified by public
.
- Note:
getDeclaredConstructors
andgetDeclaredConstructor
are not limited by access modifiers, whilegetConstructors
andgetConstructor
can only get constructors modified bypublic
.
(5) Creating Target Class Instance Objects Through Reflection
We’ve already obtained the constructor object of the target class. At this point, someone might wonder what we can do with this constructor object. Actually, we can do a lot.
First, by calling the newInstance(Object... initargs)
method in Constructor
, we can instantiate an object of the target class, for example:
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
import java.lang.reflect.Constructor;
public class ReflectionDemo {
public static void main(String[] args) throws Exception{
Class c = User.class;
Constructor con = c.getDeclaredConstructor(String.class,int.class);
User kun = (User) con.newInstance("Kun Kun", 18);
System.out.println(kun);
}
}
Someone might ask, isn’t this unnecessary? Couldn’t I just do new User("Kun Kun",18)
directly? Why make it so complicated?
Now I’ll talk about something interesting. We observe that in the User
class, there’s a constructor modified by private
. Executing the following code would definitely cause an error:
User user = new User("Kun Kun");
This is because private User(String name)
is private and cannot be directly accessed externally. However, through reflection, we can temporarily bypass access permissions to call this constructor. Call setAccessible(boolean)
in Constructor
, as follows:
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
import java.lang.reflect.Constructor;
public class ReflectionDemo {
public static void main(String[] args) throws Exception{
Class c = User.class;
Constructor con = c.getDeclaredConstructor(String.class);
con.setAccessible(true);
System.out.println(con.newInstance("Kun Kun"));
}
}
- Note: Using
con.setAccessible(true);
only temporarily modifies access permissions and doesn’t affect the target class. The constructor remainsprivate
.
(6) Getting Member Variables of Target Class Through Reflection
Since we can get constructor information of the target class through reflection, we can certainly also get member variable (Field) information of the target class.
Actually, similar to getting constructors, getting member variables is basically similar:
Calling the getDeclaredFields()
method in Field
can get all member variable information under the target class; calling getDeclaredField(String name)
in Field
can get the specified variable with variable name name
among member variables; calling the getFields()
method in Field
can get all member variable information modified by public
under the target class; calling getField(String name)
in Field
can get the specified variable with variable name name
among member variables modified by public
, as follows:
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectionDemo {
public static void main(String[] args) throws Exception{
Class c = User.class;
System.out.println(c.getConstructor());
Field[] fields = c.getDeclaredFields();
for (Field f : fields) {
System.out.println(f);
}
Field name = c.getDeclaredField("name");
System.out.println(name);
System.out.println(c.getFields().length);
//Since no public member variables are set in User, the getField() method won't be demonstrated here, you can try it yourself
}
}
- Note: The parameter lists of
getDeclaredField
andgetField
methods cannot be empty.
(7) Modifying and Getting Values of Member Variables of Target Class Objects Through Reflection
In step (6), we obtained member variable information of the target class. Can we modify or get the values of member variables of target class objects based on these member variable objects? The answer is yes. We just need to use the set(Object obj, Object value)
and get(Object obj)
methods in Field
, as follows (since member variables in User
are all private
and cannot be directly modified, we need to first call setAccessible(true)
in Field
to temporarily bypass access permissions):
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
import java.lang.reflect.Field;
public class ReflectionDemo {
public static void main(String[] args) throws Exception{
Class c = User.class;
Field name = c.getDeclaredField("name");
name.setAccessible(true);
User user = new User("Kun Kun",18);
System.out.println(user);
name.set(user,"Ge Ge");
System.out.println(user);
System.out.println(name.get(user));
}
}
(9) Getting Member Methods of Target Class Through Reflection
If you’ve read the above 8 sections carefully, you should be able to guess that getting member method (Method) information also mainly has four methods:
They are getDeclaredMethods
, getDeclaredMethod
, getMethods
, and getMethod
in Method
. Their functions and precautions should be self-evident. To facilitate demonstration, I’ll first add several methods to User
, as follows:
package top.mygld.demo.pojo;
public class User {
private String name;
private int age;
public User(){}
private User(String name){
this.name = name;
}
protected User(int age){
this.age = age;
}
public User(String name, int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private void hello(){
System.out.println("Hello, User");
}
}
Then I’ll test in the main class:
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionDemo {
public static void main(String[] args) throws Exception{
Class c = User.class;
Method[] declaredMethods = c.getDeclaredMethods();
for(Method m : declaredMethods){
System.out.println(m);
}
System.out.println(c.getMethod("setName", String.class));
Method[] methods = c.getMethods();
for(Method m : methods){
System.out.println(m);
}
System.out.println(c.getDeclaredMethod("hello"));
}
}
- Note: In
getDeclaredMethod
andgetMethod
, the first parameter is the name of the member method, and from the second parameter onward, it represents the parameter types of this member method’s parameter list. If there’s no parameter list, just write the first parameter for the above methods.
(10) Calling Member Methods of Target Class Objects Through Reflection
Through reflection, we can also call the invoke(target class object, parameter list)
method in Method
to call member methods in target class objects, as follows:
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
import java.lang.reflect.Method;
public class ReflectionDemo {
public static void main(String[] args) throws Exception{
Class c = User.class;
Method setName = c.getMethod("setName", String.class);
User user = (User) c.newInstance();
System.out.println(user);
setName.invoke(user, "Kun Kun");
System.out.println(user);
}
}
3. The Role of Reflection
i) Basic role: Can get all components of a class and then operate on them. (Already demonstrated in the above steps)
ii) Can break encapsulation. (Because it can modify access permissions of internal members of a class, it breaks class encapsulation)
iii) Can bypass generic constraints, for example, the execution result of the following code:
package top.mygld.demo.test;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
ArrayList<String> a = new ArrayList<>();
a.add("hello");
Class c = a.getClass();
Method m = c.getDeclaredMethod("add", Object.class);
m.invoke(a, 123);
System.out.println(a);
}
}
4. Summary
In summary, the core knowledge points of Java reflection mainly include four aspects: class objects (getting Class
objects and their information), constructors (getting and operating class construction methods), member variables (getting and operating class fields), and member methods (getting and calling class methods).
Many frameworks are implemented through reflection methods, such as Spring Boot
.
Through reflection, we can also design some frameworks ourselves. If I want to get member variable information of any class, I can write a relatively simple framework function, as follows:
package top.mygld.demo.test;
import top.mygld.demo.pojo.User;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
User user = new User("Kun Kun",18);
getInformation(user);
}
public static void getInformation(Object object) throws IllegalAccessException {
Class c = object.getClass();
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.getName() + ":" + field.get(object));
}
}
}
Of course, the above is just an example. This function code might not be applicable to some other classes.
Why is “reflection” called “reflection”? I think that normally calling a member property or member method of an object follows the format object.member
, while in reflection the format is basically member.method(object)
, which is the reverse of the normal format, so it’s called reflection
. Of course, this is just my personal understanding. If you’re interested in why it’s really called “reflection”, you can look up some materials~