The java new Keyword: Object Creation in Java
Learn how the java new keyword allocates memory, invokes constructors, and returns references. Understand common pitfalls and performance implications.
java new keyword requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
The new keyword in Java is the primary way to create objects. When you write new SomeClass(), the JVM allocates memory on the heap, runs the class's constructor, and returns a reference to the new instance. This article explains the mechanics, common mistakes, and performance tradeoffs of using new.
What Happens When You Use new
At the bytecode level, object creation involves several steps. The JVM first executes the new instruction, which allocates memory for the object on the heap and places a reference to that memory on the operand stack. Next, the dup instruction duplicates that reference so it can be used both for the constructor call and for the subsequent assignment. The invokespecial instruction then invokes the constructor, typically <init>, which initializes the object's fields. Finally, astore stores the reference into a local variable or array element.
This sequence explains why the constructor runs after memory is allocated. The reference is not available to your code until the constructor completes. If the constructor throws an exception, the partially initialized object becomes eligible for garbage collection, and no reference escapes the new expression.
Basic Syntax and Object Creation
The simplest form is:
Person person = new Person();
This creates a Person instance using the class's no-argument constructor. You can also create arrays with new:
int[] numbers = new int[10]; String[] names = new String[5];
For arrays, new allocates contiguous memory for the specified number of elements. Reference-type arrays are initialized to null, and primitive arrays are initialized to their default values (e.g., 0 for int, false for boolean).
You can also use new to create an instance of an anonymous class:
Runnable task = new Runnable() { @Override public void run() { System.out.println("Running"); } };
Here, new creates an instance of an anonymous subclass of Runnable. The syntax combines class definition and instantiation in one expression.
Constructors and Initialization
Every class has at least one constructor. If you do not declare any, the compiler adds a default no-argument constructor that calls super(). When you use new, the chosen constructor runs after field initializers and instance initializer blocks.
public class Book { private String title; private int pages; public Book(String title, int pages) { this.title = title; this.pages = pages; } }
You create a Book with:
Book b = new Book("Java", 400);
Constructors can call other constructors in the same class using this(...), and they must call a superclass constructor using super(...) as the first statement. The new keyword does not directly control this chaining; it simply triggers the constructor you specify, which then handles delegation.
Common Mistakes and Misunderstandings
A frequent mistake is forgetting new when calling a constructor. For example, String s = String("hello") is invalid because String is a class, not a method. The correct form is new String("hello"), though in practice you would use a string literal.
Another misunderstanding involves reference semantics. When you write:
Person a = new Person("Alice"); Person b = a;
b and a refer to the same object. Modifying b changes what a sees. The new keyword creates a distinct object each time it is evaluated, so new Person("Alice") twice produces two independent instances.
A third issue is using new with primitives. Primitives like int, double, and boolean are not objects and cannot be created with new. You must use wrapper classes such as Integer, Double, or Boolean if you need an object representation.
Memory and Performance Considerations
Every new expression allocates memory on the heap. Allocation itself is fast on modern JVMs, but it does add pressure on the garbage collector. Creating many short-lived objects in a tight loop can lead to frequent minor GC cycles, which may affect throughput.
Consider this example:
for (int i = 0; i < 100000; i++) { Point p = new Point(i, i); // use p }
Each iteration creates a new Point. If the loop runs frequently, the JVM may spend significant time reclaiming these objects. In such cases, reusing a mutable object or using a primitive-based representation can reduce allocation overhead. However, you should measure before optimizing. Modern JVMs use escape analysis and scalar replacement to eliminate some allocations that do not escape the method, so the actual cost may be lower than expected.
Object pooling is another option, but it adds complexity and can hurt performance if not implemented carefully. Only consider pooling for objects that are expensive to create, such as database connections or threads, not for simple data holders.
Alternatives to new and When to Use Them
Factory methods are a common alternative to direct new calls. For example, Integer.valueOf(int) may return a cached instance instead of creating a new one. Similarly, Collections.emptyList() returns a singleton rather than allocating a new list each time.
List<String> empty = Collections.emptyList();
Factories can hide the decision of whether to create a new object or reuse an existing one. They also allow you to change the implementation without affecting callers. Dependency injection frameworks like Spring use reflection or bytecode generation to create objects, but the underlying mechanism still involves something equivalent to new.
Reflection provides Class.newInstance() and Constructor.newInstance(), but these are slower than direct new because they perform runtime checks and bypass compile-time type safety. Use them only when the class is not known until runtime, such as in plugin systems or serialization.
When new Is Not the Right Choice
Some design patterns deliberately avoid direct new calls. A singleton, for example, ensures only one instance exists by making the constructor private and exposing a static factory method:
public class Config { private static final Config INSTANCE = new Config(); private Config() {} public static Config getInstance() { return INSTANCE; } }
Here, new is used once inside the class, but external code cannot call it directly. This gives you control over the instance lifecycle.
Static utility classes, such as Math or Collections, have private constructors and only expose static methods. There is no reason to create instances of them, so new is not used at all.
Immutable value objects often provide static factory methods like of or from to improve readability and allow caching. For instance, LocalDate.of(2025, 1, 1) is clearer than new LocalDate(2025, 1, 1) and gives the implementation freedom to return a cached instance.
The decision to use new directly versus a factory depends on whether you need to control instance creation, hide implementation details, or enforce invariants. Direct new is appropriate when the class is a simple data holder and you do not need indirection. Factories become valuable when construction logic is complex or when you want to return different subclasses based on input.