Java Reflection Explained in Detail

Table of Contents- What is the Reflection Mechanism?

  • How to Use Reflection
    • 2.1 Get Package and Class Name of an Object
    • 2.2 Obtain the Class Object
    • 2.3 Create an Instance of a Specified Type
    • 2.4 Instantiate Objects Using Constructor Objects
    • 2.5 Retrieve Interfaces Implemented by a Class
    • 2.6 Get Information About the Parent Class
    • 2.7 Access Public and Private Fields and Modify Them
    • 2.8 Access and Invoke Public and Private Methods
  • Advantages and Disadvantages of Reflection
    • 3.1 Advantages
    • 3.2 Disadvantages

Many people ask, "Can you explain reflection? What exactly is it? How is it implemented? What can we do with it? What are its advantages and disadvantages?" Let's explore these questions.

What is the Reflection Mechanism?


What is reflection? What is the meaning of 'reflection' and 'normal'?
There is a 'reflection' and a 'normal'. Normally, if we want to create an object, we use the following statement:

Person person = new Person();


When this statement is executed for the first time, the JVM loads Person.class, and after loading into memory, a Class object is created in the method area/heap for this Person class. There is some debate about whether it is in the method area or heap. Personally, I believe it should be in the method area as per the JVM specification, but different versions of the JVM may vary. For more details, refer to this link:
https://www.cnblogs.com/xy-nb/p/6773051.html
The above normal object initialization method can be considered as "normal", where a Class object is used to create a Person object.

Reflection is the opposite, where from a Person object, we obtain the Class object and then perform various operations such as initialization or method invocation.

In runtime, it allows us to construct any class's object, get the class information of any object, and access members such as variables and methods. It can be understood as having the ability to dynamically load objects and analyze and use the basic information of objects.

Key features include:

  • Determine the class of an object at runtime.
  • Construct any class's object at runtime.
  • Retrieve member variables and methods defined in a class at runtime.
  • Call any object's methods at runtime.
  • Generate dynamic proxies.

It is flexible and powerful, allowing components to be assembled at runtime without source code linking, but improper usage may impact performance. All class objects are instances of Class.

Since we can configure the fully qualified name of a class, methods, and parameters to initialize objects, it increases Java's configurability.

It is important to note that: a class itself is also an object, and methods are also objects. In Java, everything is an object except primitive data types.

How to Use Reflection


2.1 Get Package and Class Name of an Object

package invocation;
public class MyInvocation {
    public static void main(String[] args) {
        getClassNameTest();
    }
    
    public static void getClassNameTest(){
        MyInvocation myInvocation = new MyInvocation();
        System.out.println("class: " + myInvocation.getClass());
        System.out.println("simpleName: " + myInvocation.getClass().getSimpleName());
        System.out.println("name: " + myInvocation.getClass().getName());
        System.out.println("package: " +
                "" + myInvocation.getClass().getPackage());
    }
}


Output:

class: class invocation.MyInvocation
simpleName: MyInvocation
name: invocation.MyInvocation
package: package invocation


From the output, we can see:

  1. getClass(): prints with class + full class name
  2. getClass().getSimpleName(): prints only the class name
  3. getName(): prints the full clas name
  4. getClass().getPackage(): prints package + package name

getClass() returns an object, and getPackage() also returns an object.

2.2 Obtain the Class Object

In Java, everything is an object. Java can be divided into two types of objects: instance objects and Class objects. Here, obtaining the Class object refers to the second type. The Class object represents the type information of each class at runtime. For example, if there is a Student class, we use Student student = new Student() to create an instance. At this point, the information of the Student class is stored in an object, which is the Class object, and the student instance object is associated with this Class object.

We have three ways to obtain the Class object of a class at runtime, namely:

  • Class.forName("com.Student")
  • student.getClass()
  • Student.class

Example code:

package invocation;

public class MyInvocation {
    public static void main(String[] args) {
        getClassTest();
    }
    public static void getClassTest(){
        Class<?> invocation1 = null;
        Class<?> invocation2 = null;
        Class<?> invocation3 = null;
        try {
            // Most commonly used method
            invocation1 = Class.forName("invocation.MyInvocation");
        }catch (Exception ex){
            ex.printStackTrace();
        }
        invocation2 = new MyInvocation().getClass();
        invocation3 = MyInvocation.class;
        System.out.println(invocation1);
        System.out.println(invocation2);
        System.out.println(invocation3);
    }
}


The output is as follows, with all three being the same:

class invocation.MyInvocation
class invocation.MyInvocation
class invocation.MyInvocation


2.3 Create an Instance of a Specified Type

First, we have a Student class, which will be used throughout and not repeated.

class Student{
    private int age;

    private String name;

    public Student() {
    }
    public Student(int age) {
        this.age = age;
    }

    public Student(String name) {
        this.name = name;
    }
    
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }


We can use newInstance() to create a Student object:

    public static void getInstanceTest() {
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Student student = (Student) stduentInvocation.newInstance();
            student.setAge(9);
            student.setName("Hahs");
            System.out.println(student);

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
    
    
Output:
Student{age=9, name='Hahs'}


However, if we remove the no-argument constructor, we will encounter the following error:

java.lang.InstantiationException: invocation.Student
	at java.lang.Class.newInstance(Class.java:427)
	at invocation.MyInvocation.getInstanceTest(MyInvocation.java:40)
	at invocation.MyInvocation.main(MyInvocation.java:8)
Caused by: java.lang.NoSuchMethodException: invocation.Student.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.newInstance(Class.java:412)
	... 2 more


This is because we have overridden the constructor and only have parameterized constructors. If no constructor is written, every class has a default no-argument constructor. Overriding removes this, so when calling newInstance(), an error occurs indicating no such method exists. Note that newInstance() is a no-argument constructor.

2.4 Instantiate Objects Using Constructor Objects

In addition to the newInstance() method, we can also use constructor objects to create instances. This means obtaining the constructor object rather than the constructer itself. A constructor is essentially an object, and we first obtain the constructor object, which can then be used to create instances.

We can first retrieve all constructors of a class and then iterate through them:

    public static void testConstruct(){
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Constructor<?> cons[] = stduentInvocation.getConstructors();
            for(int i=0;i<cons.length;i++){
                System.out.println(cons[i]);
            }

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }


Output:

public invocation.Student(int,java.lang.String)
public invocation.Student(java.lang.String)
public invocation.Student(int)
public invocation.Student()


We can obtain various information about a constructor, including parameters, number of parameters, and types:

    public static void constructGetInstance() {
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Constructor<?> cons[] = stduentInvocation.getConstructors();
            Constructor constructors = cons[0];
            System.out.println("name: " + constructors.getName());
            System.out.println("modifier: " + constructors.getModifiers());
            System.out.println("parameterCount: " + constructors.getParameterCount());
            System.out.println("Constructor parameter types are as follows:");
            for (int i = 0; i < constructors.getParameterTypes().length; i++) {
                System.out.println(constructors.getParameterTypes()[i].getName());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }


Output, modifier is the access modifier, 1 indicates public. We can know that the retrieved constructor has two parameters, the first is int, and the second is String type. It seems that the order of retrieval may not match the order in the code.

name: invocation.Student
modifier: 1
parameterCount: 2
Constructor parameter types are as follows:
int
java.lang.String


Now that we have obtained the constructor object, can we use it to create an object? The answer is definitely yes!
Below, we create objects using different constructors:

    public static void constructGetInstanceTest() {
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Constructor<?> cons[] = stduentInvocation.getConstructors();
            // Four constructors were defined
            Student student1 = (Student) cons[0].newInstance(9,"Sam");
            Student student2 = (Student) cons[1].newInstance("Sam");
            Student student3 = (Student) cons[2].newInstance(9);
            Student student4 = (Student) cons[3].newInstance();
            System.out.println(student1);
            System.out.println(student2);
            System.out.println(student3);
            System.out.println(student4);

        } catch (Exception ex) {
            ex.printStackTrace();
        }


Output:

Student{age=9, name='Sam'}
Student{age=0, name='Sam'}
Student{age=9, name='null'}
Student{age=0, name='null'}


The order of the constructors must be matched one by one, otherwise it will result in the following parameter mismatch error:

java.lang.IllegalArgumentException: argument type mismatch
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at invocation.MyInvocation.constructGetInstanceTest(MyInvocation.java:85)
	at invocation.MyInvocation.main(MyInvocation.java:8)


2.5 Retrieve Interfaces Implemented by a Class

Using reflection, we can retrieve interface methods. If we know that a certain class implements interface methods, we can also call the interface methods by creating an object with the class name.

First, define two interfaces, InSchool:

public interface InSchool {
    public void attendClasses();
}


and AtHome:

public interface AtHome {
    public void doHomeWork();
}


Create a class Student.java that implements both interfaces:

public class Student implements AtHome, InSchool {
    public void doHomeWork() {
        System.out.println("I am a student,I am doing homework at home");
    }

    public void attendClasses() {
        System.out.println("I am a student,I am attend class in school");
    }
}


Test code:

public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> studentClass = Class.forName("invocation.Student");
        Class<?>[] interfaces = studentClass.getInterfaces();
        for (Class c : interfaces) {
            // Get interface
            System.out.println(c);
            // Get methods of the interface
            Method[] methods = c.getMethods();
            // Traverse methods of the interface
            for (Method method : methods) {
                // Create an object via reflection
                Student student = (Student) studentClass.newInstance();
                // Call the method via reflection
                method.invoke(student, null);
            }
        }
    }
}


The output shows that we can retrieve the array of interfaces, and the order is the order of inheritance. Through the Class object of the interface, we can obtain the methods of the interface and then call the methods of the implementation class. Since this is a method without parameters, we just need to pass null.

2.6 Get Information About the Parent Class

Mainly use the getSuperclass() method to get the parent class. You can also get the methods of the parent class and execute them. First, create an Animal.java:

public class Animal {
    public void doSomething(){
        System.out.println("animal do something");
    }
}


Dog.java inherits from Animal.java:

public class Dog extends Animal{
    public void doSomething(){
        System.out.println("Dog do something");
    }
}


We can create a Dog object via reflection, get its parent class Animal, and create an object, and also get the default parent class Object of Animal:

public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> dogClass = Class.forName("invocation02.Dog");
        System.out.println(dogClass);
        invoke(dogClass);

        Class<?> animalClass = dogClass.getSuperclass();
        System.out.println(animalClass);
        invoke(animalClass);

        Class<?> objectClass = animalClass.getSuperclass();
        System.out.println(objectClass);
        invoke(objectClass);
    }

    public static void invoke(Class<?> myClass) throws Exception {
        Method[] methods = myClass.getMethods();
        // Traverse methods
        for (Method method : methods) {
            if (method.getName().equalsIgnoreCase("doSomething")) {
                // Call the method via reflection
                method.invoke(myClass.newInstance(), null);
            }
        }
    }
}


Output:

2.7 Access Public and Private Fields and Modify Them

Create a Person.java with static variables, non-static variables, and properties with different modifiers like public, protected, and private.

public class Person {

    public static String type ;

    private static String subType ;

    // Name (public)
    public String name;

    protected String gender;

    private String address;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}


Use getFields() to get public properties, including static properties, and getDeclaredFields() to get all declared properties, regardless of their modifiers.

To modify public properties, simply use field.set(object, value). However, modifying private properties directly will cause the following error.

Exception in thread "main" java.lang.IllegalAccessException: Class invocation03.Tests can not access a member of class invocation03.Person with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Field.set(Field.java:761)
	at invocation03.Tests.main(Tests.java:21)


What should be done? By default, private properties cannot be accessed from outside. We can use field.setAccessible(true); to open access permissions.

For static properties, the process is similar to non-static ones, but how do we retrieve them? If it is a public modifier, it can be directly accessed via the class name. If it is a private modifier, we need to use filed.get(object), which works for all properties.

Test code:

public class Tests {
    public static void main(String[] args) throws Exception{
        Class<?> personClass = Class.forName("invocation03.Person");
        Field[] fields = personClass.getFields();
        // Get public properties
        for(Field field:fields){
            System.out.println(field);
        }
        System.out.println("=================");
        // Get all declared properties
        Field[] declaredFields = personClass.getDeclaredFields();
        for(Field field:declaredFields){
            System.out.println(field);
        }
        System.out.println("=================");
        Person person = (Person) personClass.newInstance();
        person.name = "Sam";
        System.out.println(person);

        // Modify public property
        Field fieldName = personClass.getDeclaredField("name");
        fieldName.set(person,"Jone");

        // Modify private property
        Field addressName = personClass.getDeclaredField("address");
        // Need to change access permission
        addressName.setAccessible(true);
        addressName.set(person,"东风路47号");
        System.out.println(person);

        // Modify static public property
        Field typeName = personClass.getDeclaredField("type");
        typeName.set(person,"人类");
        System.out.println(Person.type);

        // Modify static private property
        Field subType = personClass.getDeclaredField("subType");
        subType.setAccessible(true);
        subType.set(person,"黄种人");
        System.out.println(subType.get(person));
    }
}


Result:

From the results, we can see that whether it is public, protected, or private modifiers, we can query and modify them via reflection, whether they are static or non-static variables. getDeclaredField() can retrieve all declared properties, while getFields() can only retrieve public properties. For non-public properties, we need to modify their access rights: field.setAccessible(true).

To retrieve property values, use field.get(object). It is worth noting that: each property is itself an object.

2.8 Access and Invoke Public and Private Methods

Since we can access public and private properties, executing public and private methods should not be a problem.

Let's learn about it now...

First, define a class with various modifiers, whether it contains parameters, and whether it is a static method, Person.java:

public class Person {
    // Non-static public no arguments
    public void read(){
        System.out.println("reading...");
    }

    // Non-static public no arguments with return
    public String getName(){
        return "Sam";
    }

    // Non-static public with arguments   
    public int readABookPercent(String name){
        System.out.println("read "+name);
        return 80;
    }

    // Private with return value
    private String getAddress(){
        return "东方路";
    }

    // Public static no arguments no return
    public static void staticMethod(){
        System.out.println("static public method");
    }

    // Public static with arguments
    public static void staticMethodWithArgs(String args){
        System.out.println("static public method:"+args);
    }

    // Private static method
    private static void staticPrivateMethod(){
        System.out.println("static private method");
    }
}


First, let's look at retrieving all methods inside:

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("=============================================");
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
    }
}


The output is as follows:
Hmm, we see that getMethods() indeed retrieves all public methods, but there is a problem, that it also retrieves those from the parent class, which is shown in the green box in the image. We know that all classes default inherit from Object, so it retrieves all the methods from Object.
getDeclaredMethods indeed retrieves public and private methods, whether they are static or non-static, but it does not retrieve methods from the parent class.

Now, if we want to invoke methods, let's try invoking non-static methods:

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Person person = (Person) personClass.newInstance();
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if(method.getName().equalsIgnoreCase("read")){
                method.invoke(person,null);
                System.out.println("===================");
            }else if(method.getName().equalsIgnoreCase("getName")){
                System.out.println(method.invoke(person,null));
                System.out.println("===================");
            }else if(method.getName().equalsIgnoreCase("readABookPercent")){
                System.out.println(method.invoke(person,"Sam"));
                System.out.println("===================");
            }
        }

    }
}


The output shows that method.invoke(person,null); calls a method without parameters, and method.invoke(person,"Sam") calls a method with parameters. If there are more parameters, just add them accordingly, and the return value can also be obtained.

What about private methods? Let's try it, who's afraid of whom?

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Person person = (Person) personClass.newInstance();
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if(method.getName().equalsIgnoreCase("getAddress")){
                method.invoke(person,null);
            }
        }

    }
}


The result throws an error:

Exception in thread "main" java.lang.IllegalAccessException: Class invocation03.Tests can not access a member of class invocation03.Person with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Method.invoke(Method.java:491)
	at invocation03.Tests.main(Tests.java:13)


It looks like there is no permission. Don't worry, I'll handle it. Just add:

method.setAccessible(true);


Oh, perfect solution...

Now the question is, what about static methods? Of course, we can call them using the same method, and the object can directly call the class's methods.

No problem at all. Why are there several nulls in the output? Because these functions have no return value. Idiot...

If I don't want to use the method traversal approach again, how can I get the method I want? The answer is definitely yes.

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Person person = (Person) personClass.newInstance();
        Method method = personClass.getMethod("readABookPercent", String.class);
        method.invoke(person, "唐诗三百首");
    }
}


The result is exactly the same as the previous call. I won't show the image, just one line. If this method has no parameters, just pass null or don't pass anything.

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Person person = (Person) personClass.newInstance();
        Method method = personClass.getMethod("getName",null);
        System.out.println(method.invoke(person));
    }
}


Advantages and Disadvantages of Reflection


3.1 Advantages

Reflection allows us to obtain class information, create objects, and manipulate objects without knowing which class will run. This is very convenient for expansion, so reflection is the soul of framework design. Frameworks, in order to reduce coupling, must consider expandability and avoid hardcoding types.

It reduces coupling and makes the code more flexible, determining the type at runtime and binding the object, demonstrating the polymorphic function.

3.2 Disadvantages

So good, no drawbacks? Impossible! Everything has two sides.
Even though it is very powerful, reflection requires dynamic typing, and the JVM cannot optimize this part of the code, resulting in lower execution efficiency compared to directly initializing objects. It is generally not recommended for business code.

Reflection can modify access permissions, such as accessing private methods and properties, which can break encapsulation and pose security risks. Sometimes, it can also break the singleton design.

Reflection makes the code complex and difficult to maintain, since code is mainly written for people to read, right?

This article represents my personal learning notes and records. If there are any issues, please contact the author for deletion. No one is perfect, and the article is no exception. My writing is still immature, so please don't criticize me. If there are any errors, I would appreciate your correction.

The path of technology is not about a moment, but a long journey. Even if it is slow, keep going forward.

Tags: java reflection Class object Method

Posted on Mon, 08 Jun 2026 16:41:04 +0000 by mrhappiness