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!
Table Of Contents
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.
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.
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
- You may download the source code here: com.javacodegeeks.advanced.java
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
.
Thank you good Post. :)
Can I convert to Korean language of this post?
Hi Daniel,
Thanks a lot for your comment.
I think you can get the permission by contacting JCG team.
Thanks!
Best Regards,
Andriy Redko
Thanks comment.
I understand get the permission.
Have a nice day. :)
Thank you!
It is very useful!
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 »
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 »
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.
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.
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 »
great
Fantastic courses