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