The Java Reflection API enables programmatic access to class structure and behavior at runtime. Central to this capability is the Class object, which acts as a prototype for every loaded type. When compile-time access is restricted, this prototype object provides a pathway for dynamic instantiation and manipulation.
Constructor Invocation Strategies
Reflection offers mulitple approaches for object instantiation, each serving different scenarios.
Sample Entity
public class Customer {
private int customerId;
private String fullName;
private String registrationDate;
private boolean verified;
public Customer() {}
public Customer(int id, String name, String date, boolean status) {
this.customerId = id;
this.fullName = name;
this.registrationDate = date;
this.verified = status;
}
}
Instantiation Techniques
// Conventional approach
Customer regularCustomer = new Customer();
Customer premiumCustomer = new Customer(1001, "Alice Smith", "2024-03-15", true);
// Reflection approach
Class<?> customerClass = Class.forName("com.demo.Customer");
// Method 1: Direct class instantiation (deprecated in Java 9+)
Object basicInstance = customerClass.newInstance();
// Method 2: Explicit constructor retrieval (recommended)
Constructor<?> defaultCtor = customerClass.getDeclaredConstructor();
Object standardInstance = defaultCtor.newInstance();
// Parameterized construction
Constructor<?> customCtor = customerClass.getDeclaredConstructor(
int.class, String.class, String.class, boolean.class
);
Object customInstance = customCtor.newInstance(1002, "Bob Johnson", "2024-03-20", false);
The getDeclaredConstructor() method retrieves constructors regardless of access modifiers, while getConstructor() only returns public constructors.
Method Execution at Runtime
Accessible Methods
Class<?> serviceClass = Class.forName("com.demo.NotificationService");
Object service = serviceClass.newInstance();
Method sendMethod = serviceClass.getMethod("sendNotification", String.class);
sendMethod.invoke(service, "Hello World");
Restricted Methods
Class<?> serviceClass = Class.forName("com.demo.NotificationService");
Object service = serviceClass.newInstance();
Method internalMethod = serviceClass.getDeclaredMethod("logEvent");
internalMethod.setAccessible(true);
internalMethod.invoke(service);
Dynamic Field Manipulation
Public Attributes
package com.demo.reflection;
import java.lang.reflect.Field;
public class FieldAccessor {
public static void main(String[] args) throws Exception {
Class<?> productClass = Class.forName("com.demo.reflection.Product");
Object item = productClass.newInstance();
Field nameField = productClass.getField("productName");
System.out.println("Initial: " + nameField.get(item));
nameField.set(item, "Laptop");
System.out.println("Updated: " + nameField.get(item));
}
}
Private Attributes
package com.demo.reflection;
import java.lang.reflect.Field;
public class PrivateFieldAccessor {
public static void main(String[] args) throws Exception {
Class<?> productClass = Class.forName("com.demo.reflection.Product");
Object item = productClass.newInstance();
Field priceField = productClass.getDeclaredField("price");
priceField.setAccessible(true);
System.out.println("Current price: " + priceField.get(item));
priceField.set(item, 1299.99);
System.out.println("Modified price: " + priceField.get(item));
}
}
Accessing private fields requires setAccessible(true) to override Java's visbiility checks, which may raise security exceptions if the JVM security manager denies the operation.