Skip to content
Java reflection 7 min read

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 new calls.
  • 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 new calls. 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 finds public constructors. 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. Passing Integer.class when the constructor declares int will throw NoSuchMethodException. 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-opens JVM 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);
        }
    }
}
ExceptionWhen it occurs
ClassNotFoundExceptionClass.forName() cannot find the class on the classpath
NoSuchMethodExceptionNo constructor with those parameter types exists
InstantiationExceptionThe class is abstract or is an interface
IllegalAccessExceptionThe constructor is not accessible (private/protected)
InvocationTargetExceptionThe 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:

  1. Method handle resolution — the first call looks up the constructor via the reflection metadata.
  2. Access checking — unless you cached the Constructor and called setAccessible(true) once, each call re-checks visibility.
  3. 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.

  • 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 .class files before newInstance() 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
Last updated June 13, 2026
Was this helpful?