Reflection in Java

  sonic0002        2024-06-27 07:52:12       749        0    

What is Reflection?

Reflection is a feature in Java that allows a program to obtain information about itself at runtime and dynamically manipulate the properties, methods, and constructors of classes or objects. With reflection, we can instantiate objects, call methods, and set properties without knowing the exact class name beforehand.

The core of the reflection mechanism is the Class object, which represents a class. The Java Virtual Machine (JVM) automatically creates this Class object when it loads a class.

How the JVM Creates a Class?

When we write a class and compile it, the compiler converts it into bytecode stored in a .class file. During the class loading process, the JVM uses the ClassLoader to read the .class file, load the bytecode into memory, and create the corresponding Class object based on this information. Since each class is loaded only once by the JVM, every class corresponds to a unique Class object.

Example

public class User extends People {
    public String name;
    private int age;

    private static int staticFiled = 10;
    private final String sex;
    protected String protectedFiled;

    static  {
        System.out.println("static method execution");
    }

    public User(String name,String sex) {
        this.name = name;
        this.sex = sex;
    }

    private void privateMethod() {
        System.out.println("I am private method");
    }
    public void publicMethod() {
        System.out.println("I am public method");
    }

}

public class People {
    public String publicFiled;
    private String privateFiled;
}

Three Ways to Obtain a Class Object

First Method

Obtain the class object through the class name using .class. This is done at compile time, so the type User is explicitly specified and won't cause any errors. Using this method to obtain an object does not trigger class initialization; initialization only occurs when accessing the class's static members or instances.

Class<User> userClass = User.class;

Instantiate an object:

User userInstance = userClass.getDeclaredConstructor(String.class, String.class).newInstance("张三", "男");

Second Method

Obtain the class object through an object's getClass() method. This method is suitable for obtaining the class object from an already instantiated object of a class. Note that the type is not User but a wildcard ? because the Class object is obtained from an instance of User, and the specific type of the instance can only be determined at runtime, not at compile time.

User user = new User("张三", "男");

Class<?> userClass = user.getClass();

Instantiate an object:

Constructor<?> constructor = userClass.getConstructor(String.class, String.class);

User userInstance = (User) constructor.newInstance("张三", "男");

Third Method

Use the static method Class.forName() to obtain the class object through the full path. Since the type can only be known at runtime, the type is a wildcard ?. Obtaining the class object through this method will immediately trigger class initialization.

Class<?> userClass = Class.forName("org.example.reflect.entity.User");

Create an instance:

Constructor<?> constructor = userClass.getDeclaredConstructor(String.class, String.class);

User userInstance = (User) constructor.newInstance("张三", "男");

Accessing Object Fields in Java

Getting All Public Fields

To get all public fields, including those inherited from the parent class, use getFields():

Field[] fields = user.getFields();

for (Field field : fields) {
    System.out.println(field);
}

Output

public java.lang.String org.example.reflect.entity.User.name
public java.lang.String org.example.reflect.entity.People.publicField

Getting All Declared Fields

To get all declared fields in a class, regardless of their access level, use getDeclaredFields(). This does not include fields inherited from superclasses:

Field[] fields = user.getDeclaredFields();

for (Field field : fields) {
    System.out.println(field);
}

Output

public java.lang.String org.example.reflect.entity.User.name
private int org.example.reflect.entity.User.age
private final java.lang.String org.example.reflect.entity.User.sex
protected java.lang.String org.example.reflect.entity.User.protectedField

Getting Fields from Superclass

To get fields from a superclass, use getSuperclass():

Field[] fields = user.getSuperclass().getDeclaredFields();

for (Field field : fields) {
    System.out.println(field);
}

Output:

public java.lang.String org.example.reflect.entity.People.publicField
private java.lang.String org.example.reflect.entity.People.privateField

Getting a Specific Field

To get a specific public field by name, use getField(String name). For any specific field regardless of its access level, use getDeclaredField(String name).

Handling Non-Existent Fields

Trying to access a non-existent field does not produce a compile-time error but will throw an exception at runtime:

try {
    Field nonExistentField = user.getDeclaredField("nonExistentField");
} catch (NoSuchFieldException e) {
    e.printStackTrace();
}

Output:

java.lang.NoSuchFieldException: nonExistentField

Setting Field Values

To set the value of a private static field, first make it accessible:

Class<?> userClass = Class.forName("org.example.reflect.entity.User");
Field staticField = userClass.getDeclaredField("staticField");
staticField.setAccessible(true);
System.out.println(staticField.get(null));

If the field is final, it can still be modified:

Field field = userClass.getDeclaredField("sex");
field.setAccessible(true);
field.set(obj, "女生");
System.out.println(field.get(obj));

Output:

女生

Accessing Methods

Accessing methods is similar to accessing fields:

  • getMethods() retrieves all public methods in the class and its superclasses.
  • getDeclaredMethods() retrieves all declared methods in the class, regardless of access level.
  • getMethod(String name, Class<?>... parameterTypes) retrieves a specific public method by name and parameter types.
  • getDeclaredMethod(String name, Class<?>... parameterTypes) retrieves a specific declared method by name and parameter types, regardless of access level.

Summary

From the above examples, we can see that methods prefixed with Declared (like getDeclaredField) are used to retrieve all fields or methods, regardless of their access level. In contrast, methods without Declared (like getField) only retrieve public fields or methods.

Examples:

  • getDeclaredField retrieves all fields.
  • getField retrieves all public fields.

Reflection allows bypassing access control checks. Fields and methods modified with private or final can be accessed and modified, which compromises encapsulation. Therefore, it should be used with caution.

Reference: https://segmentfault.com/a/1190000044971226

METHOD  FIELD  TUTORIAL  JAVA REFLECTION 

Share on Facebook  Share on Twitter  Share on Weibo  Share on Reddit 

  RELATED


  0 COMMENT


No comment for this article.