Fundamentals 17 min read

Mastering Java Constructors and Design Patterns: From Implicit to Singleton

This article provides a comprehensive guide to Java object creation, covering implicit, no‑argument, and argument constructors, initialization blocks, static initialization, default value guarantees, visibility rules, garbage collection, finalizers, and common construction patterns such as singleton, helper, factory, and dependency injection.

Programmer DD
Programmer DD
Programmer DD
Mastering Java Constructors and Design Patterns: From Implicit to Singleton

1. Introduction

Java, created by Sun Microsystems in 1995, is one of the most widely used general‑purpose programming languages. Its powerful libraries, runtime, simple syntax, platform independence (Write Once, Run Anywhere), and strong community make it popular among developers.

This series assumes basic Java knowledge and aims to elevate your skills with advanced concepts, using Java 7 and Java 8 syntax.

2. Instance Construction

Java is an object‑oriented language, so creating new class instances is fundamental. Constructors play a central role, and Java offers several ways to define them.

2.1 Implicit Constructor

A class without an explicit constructor still receives a default no‑argument constructor generated by the compiler.

package com.javacodegeeks.advanced.construction;
public class NoConstructor {
}

Creating an instance invokes this implicit constructor:

final NoConstructor noConstructorInstance = new NoConstructor();

2.2 No‑Argument Constructor

Explicitly defining a no‑argument constructor is the simplest way to let the compiler generate the default behavior.

package com.javacodegeeks.advanced.construction;
public class NoArgConstructor {
    public NoArgConstructor() {
        // Constructor body here
    }
}

Instantiating the class calls this constructor:

final NoArgConstructor noArgConstructor = new NoArgConstructor();

2.3 Constructor with Arguments

Parameterized constructors allow passing values during object creation.

package com.javacodegeeks.advanced.construction;
public class ConstructorWithArguments {
    public ConstructorWithArguments(final String arg1, final String arg2) {
        // Constructor body here
    }
}

Both arguments must be supplied when using new:

final ConstructorWithArguments constructorWithArguments = new ConstructorWithArguments("arg1", "arg2");

Constructors can call each other using this, reducing code duplication and providing a single initialization entry point.

public ConstructorWithArguments(final String arg1) {
    this(arg1, null);
}

2.4 Initialization Blocks

Java also supports instance initialization blocks, which run before any constructor.

package com.javacodegeeks.advanced.construction;
public class InitializationBlock {
    {
        // initialization code here
    }
}

Multiple blocks execute in the order they appear.

package com.javacodegeeks.advanced.construction;
public class InitializationBlocks {
    {
        // first block
    }
    {
        // second block
    }
}

2.5 Construction Guarantee

Java automatically initializes fields to default values (e.g., false for booleans, 0 for numeric types, null for object references).

Example demonstrating default values:

package com.javacodegeeks.advanced.construction;
public class InitializationWithDefaults {
    private boolean booleanMember;
    private byte byteMember;
    private short shortMember;
    private int intMember;
    private long longMember;
    private char charMember;
    private float floatMember;
    private double doubleMember;
    private Object referenceMember;

    public InitializationWithDefaults() {
        System.out.println("booleanMember = " + booleanMember);
        System.out.println("byteMember = " + byteMember);
        System.out.println("shortMember = " + shortMember);
        System.out.println("intMember = " + intMember);
        System.out.println("longMember = " + longMember);
        System.out.println("charMember = " + charMember);
        System.out.println("floatMember = " + floatMember);
        System.out.println("doubleMember = " + doubleMember);
        System.out.println("referenceMember = " + referenceMember);
    }
}
final InitializationWithDefaults initializationWithDefaults = new InitializationWithDefaults();

Output shows the default values:

booleanMember = false
byteMember = 0
shortMember = 0
intMember = 0
longMember = 0
charMember = 0
floatMember = 0.0
doubleMember = 0.0
referenceMember = null

2.6 Visibility

Constructors follow Java's visibility rules and can be declared public, protected, private, or package‑private to control access.

2.7 Garbage Collection

The JVM automatically reclaims memory of objects that are no longer reachable. Java’s generational GC assumes most objects become unreachable shortly after creation, but long‑lived objects can fill the old generation and trigger costly stop‑the‑world collections.

2.8 Finalizers

Finalizers resemble destructors but are generally discouraged due to unpredictability and performance issues. Since Java 7, try‑with‑resources and the AutoCloseable interface provide a safer alternative.

try (final InputStream in = Files.newInputStream(path)) {
    // code here
}

3. Static Initialization

Static initialization blocks run once when the class is first loaded.

package com.javacodegeeks.advanced.construction;
public class StaticInitializationBlock {
    static {
        // static initialization code here
    }
}

Multiple static blocks execute in the order they appear, and the JVM guarantees thread‑safe execution.

package com.javacodegeeks.advanced.construction;
public class StaticInitializationBlocks {
    static {
        // first static block
    }
    static {
        // second static block
    }
}

4. Construction Patterns

Common patterns for object creation include Singleton, Helper/Utility classes, Factory, and Dependency Injection.

4.1 Singleton

Ensures only one instance of a class exists. Naïve implementation is not thread‑safe.

package com.javacodegeeks.advanced.construction.patterns;
public class NaiveSingleton {
    private static NaiveSingleton instance;
    private NaiveSingleton() {}
    public static NaiveSingleton getInstance() {
        if (instance == null) {
            instance = new NaiveSingleton();
        }
        return instance;
    }
}

Eager (thread‑safe) version uses a static final field.

package com.javacodegeeks.advanced.construction.patterns;
public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();
    private EagerSingleton() {}
    public static EagerSingleton getInstance() {
        return instance;
    }
}

Lazy initialization with synchronization avoids early resource allocation but may reduce concurrency.

package com.javacodegeeks.advanced.construction.patterns;
public class LazySingleton {
    private static LazySingleton instance;
    private LazySingleton() {}
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

4.2 Utility/Helper Class

Non‑instantiable classes that expose static methods.

package com.javacodegeeks.advanced.construction.patterns;
public final class HelperClass {
    private HelperClass() {}
    public static void helperMethod1() {
        // method body
    }
    public static void helperMethod2() {
        // method body
    }
}

4.3 Factory Pattern

Encapsulates object creation. Simple static factory method example:

package com.javacodegeeks.advanced.construction.patterns;
public class Book {
    private Book(final String title) {}
    public static Book newBook(final String title) {
        return new Book(title);
    }
}

Interface‑based factories allow multiple implementations.

public interface BookFactory {
    Book newBook();
}

public class Library implements BookFactory {
    @Override
    public Book newBook() {
        return new PaperBook();
    }
}

public class KindleLibrary implements BookFactory {
    @Override
    public Book newBook() {
        return new KindleBook();
    }
}

4.4 Dependency Injection

Dependencies should be supplied externally (e.g., via constructors) rather than created inside the class, improving testability and flexibility.

package com.javacodegeeks.advanced.construction.patterns;
public class Dependant {
    private final DateFormat format;
    public Dependant(final DateFormat format) {
        this.format = format;
    }
    public String format(final Date date) {
        return format.format(date);
    }
}

The article continues with further exploration of the Object class methods such as equals, hashCode, toString, and clone.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

SingletonInitializationgarbage-collectionConstructorsdependency-injectiondesign-patterns
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.