Skip to content

Java Reflection Review

Published: at 12:03 PM
Loading...

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);
    }
}

image.png

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());
    }
}

image.png

(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());
        }
    }
}

image.png

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));
    }
}

image.png

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.

(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);
    }
}

image.png

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"));
    }
}

(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
    }
}

(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));
    }
}

image.png

(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"));

    }
}

image.png

(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);
    }
}

image.png

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);
    }
}

image.png

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));
        }
    }
}

image.png

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~


Previous Post
Java Dynamic Proxy
Next Post
AI Grand Judge