Core Java

How to create and destroy objects

This article is part of our Academy Course titled Advanced Java.

This course is designed to help you make the most effective use of Java. It discusses advanced topics, including object creation, concurrency, serialization, reflection and many more. It will guide you through your journey to Java mastery! Check it out here!

1. Introduction

Java programming language, originated in Sun Microsystems and released back in 1995, is one of the most widely used programming languages in the world, according to TIOBE Programming Community Index. Java is a general-purpose programming language. It is attractive to software developers primarily due to its powerful library and runtime, simple syntax, rich set of supported platforms (Write Once, Run Anywhere – WORA) and awesome community.

In this tutorial we are going to cover advanced Java concepts, assuming that our readers already have some basic knowledge of the language. It is by no means a complete reference, rather a detailed guide to move your Java skills to the next level.

Along the course, there will be a lot of code snippets to look at. Where it makes sense, the same example will be presented using Java 7 syntax as well as Java 8 one.

2. Instance Construction

Java is object-oriented language and as such the creation of new class instances (objects) is, probably, the most important concept of it. Constructors are playing a central role in new class instance initialization and Java provides a couple of favors to define them.

2.1. Implicit (Generated) Constructor

Java allows to define a class without any constructors but it does not mean the class will not have any. For example, let us consider this class:

package com.javacodegeeks.advanced.construction;

public class NoConstructor {
}

This class has no constructor but Java compiler will generate one implicitly and the creation of new class instances will be possible using new keyword.

final NoConstructor noConstructorInstance = new NoConstructor();

2.2. Constructors without Arguments

The constructor without arguments (or no-arg constructor) is the simplest way to do Java compiler’s job explicitly.

package com.javacodegeeks.advanced.construction;

public class NoArgConstructor {
    public NoArgConstructor() {
        // Constructor body here
    }
}

This constructor will be called once new instance of the class is created using the new keyword.

final NoArgConstructor noArgConstructor = new NoArgConstructor();

2.3. Constructors with Arguments

The constructors with arguments are the most interesting and useful way to parameterize new class instances creation. The following example defines a constructor with two arguments.

package com.javacodegeeks.advanced.construction;

public class ConstructorWithArguments {
    public ConstructorWithArguments(final String arg1,final String arg2) {
        // Constructor body here
    }
}

In this case, when class instance is being created using the new keyword, both constructor arguments should be provided.

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

Interestingly, the constructors can call each other using the special this keyword. It is considered a good practice to chain constructors in such a way as it reduces code duplication and basically leads to having single initialization entry point. As an example, let us add another constructor with only one argument.

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

2.4. Initialization Blocks

Java has yet another way to provide initialization logic using initialization blocks. This feature is rarely used but it is better to know it exists.

package com.javacodegeeks.advanced.construction;

public class InitializationBlock {
    {
        // initialization code here
    }
}

In a certain way, the initialization block might be treated as anonymous no-arg constructor. The particular class may have multiple initialization blocks and they all will be called in the order they are defined in the code. For example:

package com.javacodegeeks.advanced.construction;

public class InitializationBlocks {
    {
        // initialization code here
    }

    {
        // initialization code here
    }

}

Initialization blocks do not replace the constructors and may be used along with them. But it is very important to mention that initialization blocks are always called before any constructor.

package com.javacodegeeks.advanced.construction;

public class InitializationBlockAndConstructor {
    {
        // initialization code here
    }
    
    public InitializationBlockAndConstructor() {
    }
}

2.5. Construction guarantee

Java provides certain initialization guarantees which developers may rely on. Uninitialized instance and class (static) variables are automatically initialized to their default values.

TypeDefault Value
booleanFalse
byte0
short0
int0
long0L
char\u0000
float0.0f
double0.0d
object referencenull

Table 1

Let us confirm that using following class as a simple example:

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 = " + 
            Character.codePointAt( new char[] { charMember }, 0  ) );
        System.out.println( "floatMember = " + floatMember );
        System.out.println( "doubleMember = " + doubleMember );
        System.out.println( "referenceMember = " + referenceMember );
    }
}

Once instantiated using new keyword:

final InitializationWithDefaults initializationWithDefaults = new InitializationWithDefaults(), 

The following output will be shown in the console:

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

2.6. Visibility

Constructors are subject to Java visibility rules and can have access control modifiers which determine if other classes may invoke a particular constructor.

ModifierPackageSubclassEveryone Else
publicaccessibleaccessibleaccessible
protectedaccessibleaccessiblenot accessible
<no modifier>accessiblenot accessiblenot accessible
privatenot accessiblenot accessiblenot accessible

Table 2

2.7. Garbage collection

Java (and JVM in particular) uses automatic garbage collection. To put it simply, whenever new objects are created, the memory is automatically allocated for them. Consequently, whenever the objects are not referenced anymore, they are destroyed and their memory is reclaimed.

Java garbage collection is generational and is based on assumption that most objects die young (not referenced anymore shortly after their creation and as such can be destroyed safely). Most developers used to believe that objects creation in Java is slow and instantiation of the new objects should be avoided as much as possible. In fact, it does not hold true: the objects creation in Java is quite cheap and fast. What is expensive though is an unnecessary creation of long-lived objects which eventually may fill up old generation and cause stop-the-world garbage collection.

2.8. Finalizers

So far we have talked about constructors and objects initialization but have not actually mentioned anything about their counterpart: objects destruction. That is because Java uses garbage collection to manage objects lifecycle and it is the responsibility of garbage collector to destroy unnecessary objects and reclaim the memory.

However, there is one particular feature in Java called finalizers which resemble a bit the destructors but serves the different purpose of performing resources cleanup. Finalizers are considered to be a dangerous feature (which leads to numerous side-effects and performance issues). Generally, they are not necessary and should be avoided (except very rare cases mostly related to native objects). A much better alternative to finalizers is the introduced by Java 7 language construct called try-with-resources and AutoCloseable interface which allows to write clean code like this:

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

3. Static initialization

So far we have looked through class instance construction and initialization. But Java also supports class-level initialization constructs called static initializers. There are very similar to the initialization blocks except for the additional static keyword. Please notice that static initialization is performed once per class-loader. For example:

package com.javacodegeeks.advanced.construction;

public class StaticInitializationBlock {
    static {
        // static initialization code here
    }
}

Similarly to initialization blocks, you may include any number of static initializer blocks in the class definition and they will be executed in the order in which they appear in the code. For example:

package com.javacodegeeks.advanced.construction;

public class StaticInitializationBlocks {
    static {
        // static initialization code here
    }

    static {
        // static initialization code here
    }
}

Because static initialization block can be triggered from multiple parallel threads (when the loading of the class happens in the first time), Java runtime guarantees that it will be executed only once and in thread-safe manner.

4. Construction Patterns

Over the years a couple of well-understood and widely applicable construction (or creation) patterns have emerged within Java community. We are going to cover the most famous of them: singleton, helpers, factory and dependency injection (also known as inversion of control).

4.1. Singleton

Singleton is one of the oldest and controversial patterns in software developer’s community. Basically, the main idea of it is to ensure that only one single instance of the class could be created at any given time. Being so simple however, singleton raised a lot of the discussions about how to make it right and, in particular, thread-safe. Here is how a naive version of singleton class may look like:

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;
    }
}

At least one problem with this code is that it may create many instances of the class if called concurrently by multiple threads. One of the ways to design singleton properly (but in non-lazy fashion) is using the static final property of the class.

final property of the class.
package com.javacodegeeks.advanced.construction.patterns;

public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();
    
    private EagerSingleton() {        
    }
    
    public static EagerSingleton getInstance() {
        return instance;
    }
}

If you do not want to waste your resources and would like your singletons to be lazily created when they are really needed, the explicit synchronization is required, potentially leading to lower concurrency in a multithreaded environments (more details about concurrency in Java will be discussing in part 9 of the tutorial, Concurrency best practices).

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;
    }
}

Nowadays, singletons are not considered to be a good choice in most cases, primarily because they are making a code very hard to test. The domination of dependency injection pattern (please see the Dependency Injection section below) also makes singletons unnecessary.


 

4.2. Utility/Helper Class

The utility or helper classes are quite popular pattern used by many Java developers. Basically, it represents the non-instantiable class (with constructor declared as private), optionally declared as final (more details about declaring classes as final will be provided in part 3 of the tutorial, How to design Classes and Interfaces) and contains static methods only. For example:

package com.javacodegeeks.advanced.construction.patterns;

public final class HelperClass {
    private HelperClass() {        
    }
    
    public static void helperMethod1() {
        // Method body here
    }
    
    public static void helperMethod2() {
        // Method body here
    }
}

From seasoned software developer standpoint, such helpers often become containers for all kind of non-related methods which have not found other place to be put in but should be shared somehow and used by other classes. Such design decisions should be avoided in most cases: it is always possible to find another way to reuse the required functionality, keeping the code clean and concise.

4.3. Factory

Factory pattern is proven to be extremely useful technique in the hands of software developers. As such, it has several flavors in Java, ranging from factory method to abstract factory. The simplest example of factory pattern is a static method which returns new instance of a particular class (factory method). For 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 );
    }
}

The one may argue that it does not make a lot of sense to introduce the newBook factory method but using such a pattern often makes the code more readable. Another variance of factory pattern involves interfaces or abstract classes (abstract factory). For example, let us define a factory interface:

public interface BookFactory {
    Book newBook();
}

With couple of different implementations, depending on the library type:

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

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

Now, the particular class of the Book is hidden behind BookFactory interface implementation, still providing the generic way to create books.

4.4. Dependency Injection

Dependency injection (also known as inversion of control) is considered as a good practice for class designers: if some class instance depends on the other class instances, those dependencies should be provided (injected) to it by means of constructors (or setters, strategies, etc.) but not created by the instance itself. Let us consider the following example:

package com.javacodegeeks.advanced.construction.patterns;

import java.text.DateFormat;
import java.util.Date;

public class Dependant {
    private final DateFormat format = DateFormat.getDateInstance();
    
    public String format( final Date date ) {
        return format.format( date );
    }
}

The class Dependant needs an instance of DateFormat and it just creates one by calling DateFormat.getDateInstance() at construction time. The better design would be to use constructor argument to do the same thing:

package com.javacodegeeks.advanced.construction.patterns;

import java.text.DateFormat;
import java.util.Date;

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 );
    }
}

In this case the class has all its dependencies provided from outside and it would be very easy to change date format and write test cases for it.

5. Download the Source Code

6. What’s next

In this part of the tutorial we have looked at classes and class instances construction and initialization techniques, along the way covering several widely used patterns. In the next part we are going to dissect the Object class and usage of its well-known methods: equals, hashCode, toString and clone.

Andrey Redko

Andriy is a well-grounded software developer with more then 12 years of practical experience using Java/EE, C#/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

11 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Daniel
9 years ago

Thank you good Post. :)

Can I convert to Korean language of this post?

Andrey Redko
9 years ago
Reply to  Daniel

Hi Daniel,

Thanks a lot for your comment.
I think you can get the permission by contacting JCG team.
Thanks!

Best Regards,
Andriy Redko

Daniel
9 years ago
Reply to  Andrey Redko

Thanks comment.

I understand get the permission.

Have a nice day. :)

Marat
Marat
8 years ago

Thank you!
It is very useful!

Molnár Dániel
Molnár Dániel
8 years ago

The domination of dependency injection pattern (please see the Dependency Injection section below) also makes singletons unnecessary. Hi, thanks for the article. It is very useful. I have one question. You wrote dependency injection makes singleton pattern unnecessary. It isn’t clear for me how. If I understand well, I have to use decency injection, if my class needs another class, in this case if I initialize the depending class at the constructor, it makes easy to change it’s implementation. But how will I be able to get the same instance of this class what is the main goal of singleton… Read more »

Andriy Redko
8 years ago

Hi Molnár Dániel, Thank you very much for your comment. It is true, dependency injection makes pure singleton pattern unnecessary. The reason is that dependency container takes care by managing the lifecycle of the instances (beans) being created, taking desired scope into account. So for example, declaring the class to have “singleton” scope instructs the dependency container to create one and only one instance, and injecting it everywhere. Getting back to your example, the container will ensure the same single instance of the class will be passed to the constructor(s) of any depending classes. Essentially, that means you will never… Read more »

Oyudo Gerald
8 years ago

Thank You Andriy,

The initialization blocks are what could have made my codes better in some occasions. Sometimes you might want a functionality before calling the this constructor.

Thanks again for a useful post.

Luiz Vicente
Luiz Vicente
7 years ago

What about using enums as Singletons?
Using classes to create a singletons is ok but one could still use reflection to make the constructor public, so a simple “enum Singleton { INSTANCE; private Singleton(){} }” could be a better design approach.

Andriy Redko
7 years ago
Reply to  Luiz Vicente

Hi Luiz, Thanks for your comment. Surely, nothing prevents to use Enum as singletons, they actually are singletons by definition. Now, the question is what kind of singletons could be expressed as enumerations? Usually, Enums are used to represent the constants, immutable instances of data structures (with known ahead of time values) which may also have additional logic associated with it. However, would you use Enums to design let say service layer or data access layer for example? I am sure you won’t, Enums are very useful but do have limited applicability and also often lead to tight coupling of… Read more »

john
john
4 years ago

great

indah.yusuf
indah.yusuf
3 years ago

Fantastic courses

Back to top button