skip to content

Why is Class.newInstance() deprecated, and what is the modern way to create an instance reflectively?

level: middleimportance: must knowfreq 55%

answer

  1. Class.newInstance() smuggles checked exceptions past the compiler
  2. Deprecated since Java 9
  3. Modern: getDeclaredConstructor(types).newInstance(args)
  4. Constructor.newInstance wraps in InvocationTargetException
  5. getConstructor=public, getDeclaredConstructor=any access

basics

~10 s

Class.newInstance() is deprecated because it silently passes through any checked exception the constructor throws, breaking Java's checked-exception checking. Use clazz.getDeclaredConstructor(paramTypes).newInstance(args) instead, which wraps those exceptions in InvocationTargetException.

solid answer

~40 s

The old Class.newInstance() only works for a public no-arg constructor and has a design flaw: if that constructor throws a checked exception, newInstance() rethrows it unchanged even though its signature doesn't declare it — defeating the compiler's checked-exception guarantees. The modern replacement is to obtain a Constructor object via getConstructor(...) (public) or getDeclaredConstructor(...) (any access) and call constructor.newInstance(args...). This supports constructors with arguments, lets you setAccessible(true) for non-public ones, and crucially wraps any exception the constructor throws in InvocationTargetException — so the caller deals with a single, declared checked exception and unwraps via getCause(). Class.newInstance() has been deprecated since Java 9 for exactly this reason.

code

java · 8 lines
java
// Modern, exception-safe instantiation
Constructor<MyType> ctor = clazz.getDeclaredConstructor(String.class, int.class);
ctor.setAccessible(true); // if non-public
try {
    MyType obj = ctor.newInstance("hi", 42);
} catch (InvocationTargetException e) {
    Throwable cause = e.getCause(); // exception thrown by the constructor body
}

go deeper

for a junior

Knows to use getDeclaredConstructor().newInstance() and that Class.newInstance() is deprecated.

for a middle

Explains the checked-exception bypass that motivated deprecation and that the modern API supports args and non-public constructors.

for a senior

Articulates the full exception model (InstantiationException vs InvocationTargetException) and the module-system access constraints.

for a principal

Frames reflective instantiation in DI/framework design — caching constructors, avoiding it on hot paths, and the safety/contract reasons the JDK changed the recommendation.

## The two ways to instantiate reflectively When you need to create an object whose type is decided at runtime, you have historically had two APIs. ### The legacy way: `Class.newInstance()` ```java Object o = clazz.newInstance(); // deprecated since Java 9 ``` This only works when the class has an **accessible no-argument constructor**. It has a serious, subtle defect around **checked exceptions**. **Background — checked exceptions:** Java forces you to either catch or declare (`throws`) any *checked* exception (a subclass of `Exception` that isn't a `RuntimeException`). The compiler verifies this. It's a core part of the language's exception-handling contract. The flaw: if the no-arg constructor throws a *checked* exception, `Class.newInstance()` **rethrows that exact checked exception** even though `newInstance()` itself is *not declared* to throw it. So a checked exception appears at a call site where the compiler never required you to handle it — it has been smuggled past the compiler's checks. This is widely considered a hole in the type system, and it's the main reason the method was **deprecated in Java 9**. ### The modern way: `getDeclaredConstructor(...).newInstance(...)` ```java Constructor<MyType> ctor = clazz.getDeclaredConstructor(String.class, int.class); MyType obj = ctor.newInstance("hi", 42); ``` Step by step: 1. **Get a `Constructor` object.** `getConstructor(Class<?>...)` returns a **public** constructor matching those parameter types; `getDeclaredConstructor(Class<?>...)` returns a constructor of **any** access level. Pass an empty argument list for a no-arg constructor: `getDeclaredConstructor()`. 2. **Optionally enable access.** For a non-public constructor, call `ctor.setAccessible(true)` first (subject to the module system — the package must be open to your module on Java 9+). 3. **Call `newInstance(args...)`.** Arguments are autoboxed `Object`s; the result is the new instance (cast as needed). ### Why the modern API is better - **Supports constructors with arguments**, not just no-arg. - **Supports non-public constructors** via `setAccessible`. - **Exception correctness:** `Constructor.newInstance()` wraps *any* exception the constructor throws in **`InvocationTargetException`** (a checked exception it *does* declare). The caller unwraps via `getCause()`. No exception is smuggled past the compiler. ### Exceptions from `Constructor.newInstance()` - `InstantiationException` — the class is abstract or an interface (can't be instantiated). - `IllegalAccessException` — the constructor isn't accessible and you didn't enable access. - `IllegalArgumentException` — wrong number/types of arguments. - `InvocationTargetException` — the constructor body threw; `getCause()` is the real exception. ### Takeaway Always prefer `getDeclaredConstructor(...).newInstance(...)`. It is more capable and it preserves Java's checked-exception contract, which `Class.newInstance()` violates.

  • Beyond exceptions, name one capability getDeclaredConstructor().newInstance() has that Class.newInstance() lacks.
    It can call constructors that take arguments and non-public constructors (with setAccessible). Class.newInstance() only works for an accessible no-arg constructor.
  • What exception is thrown if you call newInstance() on the Class of an abstract class?
    InstantiationException — abstract classes and interfaces cannot be instantiated.

saying these in an interview costs you the question

  • Saying Class.newInstance() is deprecated merely because it's 'old' rather than its checked-exception bypass flaw.
  • Believing the replacement still requires a no-arg constructor — it supports arguments.
  • Confusing InstantiationException (abstract/interface) with InvocationTargetException (constructor threw).

context