1. Creating objects via reflection
Sometimes we need to create objects without knowing their type at compile time. For example, the class name comes from configuration, or we are writing a generic framework (serializer, DI container). In regular code we would write:
User user = new User("John", 25);
But what if we don’t know that the class is called User and don’t know the constructor parameters? This is where reflection helps.
Deprecated approach: Class.newInstance()
Previously in Java there was a method Class.newInstance() that created an object via a public no‑arg constructor:
Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.newInstance();
IMPORTANT: This method has been marked as deprecated since Java 9 and is not recommended. It does not allow you to choose a constructor, hides the root causes of errors, and requires only a public no‑arg constructor.
Modern approach: via constructors
In real code there is often no public no‑arg constructor. The current approach is to obtain the required constructor via getConstructor(...) or getDeclaredConstructor(...), then call its newInstance(...).
Example: creating an object with parameters
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
import java.lang.reflect.Constructor;
Class<?> clazz = Class.forName("User");
// Obtain the constructor with parameters String, int
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// Create an object and pass the parameters
Object user = constructor.newInstance("John", 25);
System.out.println(user); // toString, if defined
What’s happening here?
- We obtain the required constructor by signature.
- We call newInstance(...) on it and pass arguments.
- We get an object of type Object (you can cast it to User if the type is known).
What if the constructor is private?
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true); // Magic! Now you can call a private constructor.
Object user = constructor.newInstance("John", 25);
WARNING: Don’t overuse this — it breaks encapsulation. In modular applications (Java 9+) access restrictions also apply.
Comparison table of object creation approaches
| Approach | Speed | Type safety | When to use |
|---|---|---|---|
|
Fastest | Full | Always, when possible |
|
Slow | Lost | Frameworks, plugins |
|
Medium | Partial | High‑performance libraries |
| Factories | Fast | Full | Flexible object creation |
2. Invoking methods via reflection
You can invoke any method of an object even if you don’t know its name at compile time, or if it is private.
Obtaining a method
import java.lang.reflect.Method;
Class<?> clazz = user.getClass();
// Obtain a public method by name and parameter types
Method method = clazz.getMethod("getName"); // No parameters
Object result = method.invoke(user); // Invoke a method without parameters
System.out.println(result); // Will print the user’s name
Invoking a method with parameters
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(user, "Peter"); // Set a new name
Invoking a private method
Method secret = clazz.getDeclaredMethod("secretMethod", int.class);
secret.setAccessible(true); // Remove access checks
Object secretResult = secret.invoke(user, 123);
Important: All parameters are passed as an array of objects (varargs). If the method returns something, the result will come back as an Object.
3. Accessing fields via reflection
Sometimes you need to read or change a field value even if it is private (for example, during serialization or testing).
Obtaining a field
import java.lang.reflect.Field;
Class<?> clazz = user.getClass();
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // If the field is not public
// Reading a value
Object age = ageField.get(user);
System.out.println("Age: " + age);
// Changing a value
ageField.set(user, 42);
System.out.println("New age: " + ageField.get(user));
Working with static fields
If the field is static, pass null instead of an instance to get/set:
Field staticField = clazz.getDeclaredField("counter");
staticField.setAccessible(true);
staticField.set(null, 100); // No instance needed for static fields
4. Limitations and exceptions
Working with reflection can involve many checked exceptions. The most common are:
- ClassNotFoundException — no class with that name was found.
- NoSuchMethodException — there is no constructor or method with that signature.
- NoSuchFieldException — field not found.
- IllegalAccessException — no access to the member (for example, a private method without setAccessible(true)).
- InstantiationException — the class is abstract or an interface; cannot create an instance.
- InvocationTargetException — an error inside the invoked constructor or method.
Handling example:
try {
// ... your reflective code ...
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
If details are not important, it’s more convenient to catch the common superclass ReflectiveOperationException (Java 7+).
5. Practice: mini program “Dynamic Instantiator”
Sample class for experiments
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private void sayHello() {
System.out.println("Hello, my name is " + name + ", I am " + age + " years old.");
}
}
Dynamic creation and manipulation
import java.lang.reflect.*;
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 1. Obtain a Class object by name
Class<?> clazz = Class.forName("Person");
// 2. Obtain the constructor and create an instance
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object person = constructor.newInstance("Alice", 30);
// 3. Invoke the private method sayHello
Method sayHello = clazz.getDeclaredMethod("sayHello");
sayHello.setAccessible(true);
sayHello.invoke(person); // Hello, my name is Alice, I am 30 years old.
// 4. Change the private field name
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(person, "Bob");
// 5. Invoke sayHello again
sayHello.invoke(person); // Hello, my name is Bob, I am 30 years old.
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
Note:
- Everything is done without direct knowledge of the Person type in the code.
- You can change private fields and invoke private methods (provided this is allowed by the JVM security policy and module configuration).
6. How does this relate to real applications?
Reflection is at the core of many popular libraries and frameworks:
- JUnit: find methods annotated with @Test, instantiate tests, and invoke methods.
- Spring: DI, bean creation, autowiring.
- Jackson, Gson: serialization/deserialization of object fields.
- Hibernate: access to entity fields, proxies, and lazy loading.
7. Visual diagram: how an object is created via reflection
flowchart TB
A["Class name (String)"] --> B["Class.forName"]
B --> C["Class<?>"]
C --> D["getConstructor(...)"]
D --> E["Constructor<?>"]
E --> F["newInstance(...)"]
F --> G["Object"]
8. Common mistakes when working with reflection
Error #1: Wrong constructor. Requesting a constructor with the wrong signature, for example, getConstructor(String.class) when only a constructor with two parameters exists — you’ll get NoSuchMethodException. Always check signatures!
Error #2: Lack of access. Invoking a private constructor/method without setAccessible(true) leads to IllegalAccessException. And remember: in Java 9+ the module system may impose additional restrictions.
Error #3: Type issues. All parameters and return values via reflection are Object. Incorrect casting — ClassCastException.
Error #4: Uncaught exceptions. Reflection throws many checked exceptions. Handle them, for example by catching the common ReflectiveOperationException, otherwise the code will not compile.
Error #5: Encapsulation violation. Mindlessly using setAccessible(true) is “access to someone else’s fridge.” Use it only when truly necessary and consider security requirements.
GO TO FULL VERSION