Creating Objects Reflectively
Normally you create objects with the new keyword — the class name is baked into your source code at compile time. Reflection lets you go further: you can load a class by name at runtime and create an instance of it without ever writing that class name in your code. This is the engine behind dependency injection frameworks, plugin systems, ORMs, and test libraries like JUnit.
Why Create Objects Reflectively?
The most common use cases are:
- Frameworks — Spring, Hibernate, and JUnit all create your objects for you based on configuration, not hard-coded
newcalls. - Plugin systems — Load a class from an external JAR and instantiate it without recompiling the host application.
- Factories driven by configuration — Read a class name from a properties file or database and create the right implementation at startup.
- Serialization / deserialization libraries — Jackson and Gson recreate objects from JSON without knowing the types at compile time.
Note: Reflection is powerful but comes at a cost — it bypasses compile-time type checks and is slower than direct
newcalls. Use it when the class name genuinely cannot be known at compile time, not as a shortcut to avoid normal code.
The Core API: Class, Constructor, and newInstance()
Everything starts with a Class<?> object. Once you have one, you can inspect and create instances of that class.
Step 1 — Obtain a Class Object
There are three ways:
// 1. Class literal (class known at compile time)
Class<?> c1 = String.class;
// 2. getClass() on an existing instance
String s = "hello";
Class<?> c2 = s.getClass();
// 3. Class.forName() — class name supplied at runtime (most common in frameworks)
Class<?> c3 = Class.forName("java.lang.String"); // throws ClassNotFoundException
Class.forName() is the one that unlocks true runtime dynamism because the class name can come from a config file, a user prompt, or a database row.
Step 2 — Get a Constructor
Call getDeclaredConstructor(...) passing the parameter types you need:
import java.lang.reflect.Constructor;
// No-arg constructor
Constructor<?> noArg = MyClass.class.getDeclaredConstructor();
// Constructor that takes (String, int)
Constructor<?> twoArg = MyClass.class.getDeclaredConstructor(String.class, int.class);
Tip:
getConstructor()only findspublicconstructors.getDeclaredConstructor()finds any constructor — public, protected, package-private, or private — which is useful when testing or when a singleton’s constructor is private.
Step 3 — Call newInstance()
Object instance = noArg.newInstance(); // no arguments
Object instance2 = twoArg.newInstance("Alice", 30); // matching arguments
Constructor.newInstance() throws InvocationTargetException if the constructor itself throws, and IllegalAccessException if the constructor is not accessible.
A Complete Working Example
import java.lang.reflect.Constructor;
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
// Class name could come from a config file at runtime
String className = "java.util.ArrayList";
Class<?> clazz = Class.forName(className);
// Get the no-arg constructor
Constructor<?> constructor = clazz.getDeclaredConstructor();
// Create a new instance
Object list = constructor.newInstance();
System.out.println("Created: " + list.getClass().getName());
System.out.println("Is ArrayList? " + (list instanceof java.util.ArrayList));
}
}
Output:
Created: java.util.ArrayList
Is ArrayList? true
Creating Objects with Parameterised Constructors
If the class has a constructor that requires arguments, you must declare the parameter types when looking it up:
import java.lang.reflect.Constructor;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
import java.lang.reflect.Constructor;
public class ReflectPerson {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("Person");
// Match the exact parameter types
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
Object person = constructor.newInstance("Alice", 30);
System.out.println(person);
}
}
Output:
Person{name='Alice', age=30}
Warning: The types you pass to
getDeclaredConstructor()must match exactly. PassingInteger.classwhen the constructor declaresintwill throwNoSuchMethodException. Use primitive types (int.class,boolean.class) for primitive parameters.
Accessing Private Constructors
Frameworks sometimes need to reach constructors that are deliberately private (for example, a singleton or a value-object factory). You can do this by calling setAccessible(true):
import java.lang.reflect.Constructor;
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {} // private constructor
public static Singleton getInstance() { return INSTANCE; }
public String greet() { return "Hello from Singleton!"; }
}
import java.lang.reflect.Constructor;
public class BreakSingleton {
public static void main(String[] args) throws Exception {
Constructor<Singleton> constructor =
Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true); // bypass access check
Singleton newInstance = constructor.newInstance();
System.out.println(newInstance.greet());
System.out.println("Same as INSTANCE? " + (newInstance == Singleton.getInstance()));
}
}
Output:
Hello from Singleton!
Same as INSTANCE? false
Warning: Using
setAccessible(true)defeats encapsulation and can break invariants (as shown above — the “singleton” is no longer a singleton). Since Java 9, the module system restricts this further; you may need--add-opensJVM flags to access types in named modules. Use with care.
The Deprecated Class.newInstance() — What Changed
Before Java 9, you might see this style:
// Old, deprecated — avoid in modern code
Object obj = MyClass.class.newInstance();
This method was deprecated in Java 9 and removed in Java 21 because it:
- Only worked with public no-arg constructors.
- Sneakily propagated checked exceptions from the constructor without declaring them.
The modern replacement is always getDeclaredConstructor().newInstance(), which is explicit about types and handles exceptions properly.
Handling Reflection Exceptions
Reflective instantiation can throw several checked and unchecked exceptions. A clean pattern is to catch them at the right level:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class SafeFactory {
public static Object create(String className) {
try {
Class<?> clazz = Class.forName(className);
Constructor<?> ctor = clazz.getDeclaredConstructor();
return ctor.newInstance();
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Class not found: " + className, e);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("No no-arg constructor in: " + className, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Constructor threw an exception", e.getCause());
} catch (InstantiationException e) {
throw new IllegalArgumentException("Cannot instantiate abstract class: " + className, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Constructor is not accessible", e);
}
}
}
| Exception | When it occurs |
|---|---|
ClassNotFoundException | Class.forName() cannot find the class on the classpath |
NoSuchMethodException | No constructor with those parameter types exists |
InstantiationException | The class is abstract or is an interface |
IllegalAccessException | The constructor is not accessible (private/protected) |
InvocationTargetException | The constructor itself threw an exception — unwrap with getCause() |
Under the Hood
When you call Class.forName("com.example.Foo"), the JVM’s class loader searches the classpath, reads the .class bytecode, and creates a Class<?> object in the metaspace (or PermGen in pre-Java 8 runtimes). This is the same loading that happens for a new call — the difference is timing: a new triggers loading at the point the JVM first reaches that instruction; Class.forName() triggers it on demand at runtime.
Constructor.newInstance() ultimately calls the same bytecode path as new — it allocates an object on the heap, zero-initialises fields, then invokes <init>. The overhead compared to a plain new comes primarily from:
- Method handle resolution — the first call looks up the constructor via the reflection metadata.
- Access checking — unless you cached the
Constructorand calledsetAccessible(true)once, each call re-checks visibility. - Boxing of arguments — arguments must be passed as
Object[], so primitives are autoboxed (see Autoboxing & Unboxing).
Since Java 7, the JVM inflates frequently-used reflective calls into direct bytecode via a mechanism called inflation. After approximately 15 invocations, the JVM generates a native accessor that is almost as fast as a direct new. For code called millions of times, consider caching the Constructor object and using MethodHandle (from java.lang.invoke) for best performance.
Generics and Type Safety
Constructor.newInstance() returns Object. You can add a type parameter to the constructor lookup to get a typed result:
// Type-safe factory using a typed Class<T>
public static <T> T create(Class<T> clazz) throws Exception {
Constructor<T> ctor = clazz.getDeclaredConstructor();
return ctor.newInstance(); // returns T, no cast needed
}
// Usage
java.util.ArrayList<String> list = create(java.util.ArrayList.class);
This is the pattern used internally by many DI frameworks to avoid unchecked casts.
Related Topics
- Reflection API — the entry point to the full Reflection API and when to use it
- Class Loaders & Class Loading — how the JVM finds and loads
.classfiles beforenewInstance()can run - Access Modifiers — the rules that
setAccessible(true)overrides, and why they exist - Constructors — the standard (non-reflective) way to initialise objects, which reflection ultimately calls
- Autoboxing & Unboxing — why passing primitive arguments to
newInstance(Object[])carries a boxing cost - Design Patterns — Factory and Singleton patterns that reflection-based frameworks rely on