Immutable objects in Java. The main aim and usage.

Stonehenge

What are immutable objects in Java

Immutable objects are multifunctional objects that can be used in different parts of the program.

For example,  immutable objects can be in a multithreaded environment for working with data. They can also be complex constants that help you avoid unnecessary memory usage and garbage collector overloading. Separately we should mention immutability when working with collections.

The main parameters of immutable classes

Saying that a class is immutable, it must correspond to the following criteria:

  • Do not provide methods that modify the object’s state. These methods can directly modify the object state of the class.
  • Make sure that the class cannot be inherited. By inheriting a class, you can easily access its fields, where you can then easily modify them.
  • Make all fields in the class final. Thus we can show our intention about whether the fields in the class can be modified.
  • Make all fields in the class private. By adding this attribute to the field, we will close off the access where you can modify the fields of the class directly.
  • Make sure that the class does not give exclusive access to modifiable objects. If, for example, there are complex objects in a class (collections, other complex classes, etc), you should consider making a copy of the field, so that it will not be possible to modify it by reference.

Practical use of immutable objects

In practice, immutability allows building more stable programs, and its principles can often be applied in the fundamental parts of the software.

For example, these can be complex constants and strings that can be used either as program configuration or as basic data.

Complex constants

There can be many static constants in a project to represent different states and statuses. If you take, for example, the basic wrapper classes of simple types, you can see that almost all of them are immutable.

For example, the Boolean class and how object states are set there:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

public static Boolean valueOf(boolean b) {
        return b ? TRUE : FALSE;
}

Also, an example with a BigInteger:

public static final BigInteger ZERO;
public static final BigInteger ONE;
public static final BigInteger TWO;
public static final BigInteger TEN;


private static final BigDecimal ZERO_THROUGH_TEN[] = {
    new BigDecimal(BigInteger.ZERO,       0,  0, 1),
    new BigDecimal(BigInteger.ONE,        1,  0, 1),
    new BigDecimal(BigInteger.TWO,        2,  0, 1),
    new BigDecimal(BigInteger.TEN,        10, 0, 2),
};

...

totalBig = totalBig.divide(BigInteger.TEN);

Java Concurrency

Sometimes when an object is processed in a multithreaded environment, you need to separate variables that need to be unique within a separate thread. There can be many reasons for this.

A ThreadLocal class can be used for this purpose. Below is an example of using local variables for each individual thread.

public class ThreadExample {
    
    public static final ThreadLocal<StateExampleHolder> statePerThreadExample = new ThreadLocal<StateExampleHolder>() {
        
        @Override
        protected StateExampleHolder initialValue() {
            return new StateExampleHolder("started");  
        }
    };

    public static StateExampleHolder getStatus() {
        return statePerThreadExample.get();
    }
}

In Collections

As you know, often, some collections consist of a key-value pair. If you take a closer look at, for example, a HashMap collection, you can see that, according to the key hash, the collection’s internal mechanism determines which bucket to put the object in.

Imagine that the key will be modifiable. Suppose you put an object in a collection with a particular key, such as “one”. Then in the process, the key object changed the hash code and the internal state. Literally, the value became “two”.

From the example above, it is obvious that we have already lost the value of the key “one” and it will be impossible to retrieve it from the collection.

In this case, immutability solves the problem of losing objects in the collection. Because the state and hash code cannot be changed, nothing is lost.

Immutable string

Strings

Strings are probably some of the most frequently used classes. We’ve already written about String Pool and how strings are stored in the memory, and we have also done a basic overview of the String class in Java.

As you know, strings themselves are immutable. Every time a string is modified, a new String object is created. So, for immutable strings, we have StringBuilder and StringBuffer.

What about the string literals? If, for example, the reference to the string changes, does the value also change? Let’s look at an example:

String str1 = "Java is the best program language in the world";

String str2 = str1;
System.out.println(str2);

str1 = "No, С++ the first";
System.out.println(str2);

Since strings are really immutable, the result will be obvious:

Java is the best program language in the world

Java is the best program language in the world

List of commonly used immutable classes in Java

Almost all classes wrappers of primitive types: Integer, String, Short, Boolean, BigInteger, BigDecimal, and others.

Those who are too lazy to read can find more information in this video:

Related posts

Leave a Comment