Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts
In a previous article Everything You Need to Know About Java Serialization, we discussed how serializability of a class is enabled by implementing the Serializable interface. If our class does not implement Serializable interface or if it is having a reference to a non Serializable class then JVM will throw NotSerializableException.

All subtypes of a serializable class are themselves serializable and Externalizable interface also extends Serializable. So even if we customize our serialization process using Externalizable our class is still a Serializable.

The Serializable interface is a marker interface which has no methods or fields and it works like a flag for the JVM. The Java serialization process provided by ObjectInputStream and ObjectOutputStream classes are fully controlled by the JVM.

But what if we want to add some additional logic to enhance this normal process, for example, we may want to encrypt/decrypt our sensitive information before serializing/deserializing it. Java provides us with some additional methods for this purpose which we are going to discuss in this blog.


Java-Serialization-Magic-Methods-And-Their-Uses

writeObject and readObject methods

Serializable classes that want to customize or add some additional logic to enhance the normal serialization/deserialization process should provide writeObject and readObject methods with these exact signatures:
  • private void writeObject(java.io.ObjectOutputStream out) throws IOException
  • private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
These methods are already discussed in great details under article Everything You Need to Know About Java Serialization.

readObjectNoData method

As described in Java docs of Serializable class, if we want to initialize the state of the object for its particular class in the event that the serialization stream does not list the given class as a superclass of the object being deserialized then we should provide writeObject and readObject methods with these exact signatures:
  • private void readObjectNoData() throws ObjectStreamException
This may occur in cases where the receiving party uses a different version of the deserialized instance's class than the sending party, and the receiver's version extends classes that are not extended by the sender's version. This may also occur if the serialization stream has tampered; hence, readObjectNoData is useful for initializing deserialized objects properly despite a "hostile" or incomplete source stream.

Each serializable class may define its own readObjectNoData method. If a serializable class does not define a readObjectNoData method, then in the circumstances listed above the fields of the class will be initialized to their default values.

writeReplace and readResolve methods

Serializable classes that need to designate an alternative object to be used when writing an object to the stream should provide this special method with the exact signature:
  • ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
And Serializable classes that need to designate a replacement when an instance of it is read from the stream should provide this special method with the exact signature:
  • ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException
Basically, the writeReplace method allows the developer to provide a replacement object that will be serialized instead of the original one. And the readResolve method is used during deserialization process to replace the de-serialized object by another one of our choices.

One of the main usages of writeReplace and readResolve methods is to implement the singleton design pattern with Serialized classes. We know that the deserialization process creates a new object every time and it can also be used as a method to deeply clone an object, which is not good if we have to make our class singleton.

You can read more about Java cloning and serialization on Java Cloning and Java Serialization topics.

The method readResolve is called after readObject has returned (conversely writeReplace is called before writeObject and probably on a different object). The object the method returns replaces this object returned to the user of ObjectInputStream.readObject and any further back-references to the object in the stream. We can use the writeReplace method to replace serializing object with null so nothing will be serialized and then use the readResolve method to replace the deserialized object with the singleton instance.

validateObject method

If we want to perform certain validations on some of our fields we can do that by implementing ObjectInputValidation interface and overriding validateObject method from it.

Method validateObject will automatically get called when we register this validation by calling ObjectInputStream.registerValidation(this, 0) from readObject method. It is very useful to verify the stream has not been tampered with, or that the data makes sense before handing it back to your application.

Below example covers code for all above methods

public class SerializationMethodsExample {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Employee emp = new Employee("Naresh Joshi", 25);
        System.out.println("Object before serialization: " + emp.toString());

        // Serialization
        serialize(emp);

        // Deserialization
        Employee deserialisedEmp = deserialize();
        System.out.println("Object after deserialization: " + deserialisedEmp.toString());


        System.out.println();

        // This will print false because both object are separate
        System.out.println(emp == deserialisedEmp);

        System.out.println();

        // This will print false because both `deserialisedEmp` and `emp` are pointing to same object,
        // Because we replaced de-serializing object in readResolve method by current instance
        System.out.println(Objects.equals(emp, deserialisedEmp));
    }

    // Serialization code
    static void serialize(Employee empObj) throws IOException {
        try (FileOutputStream fos = new FileOutputStream("data.obj");
             ObjectOutputStream oos = new ObjectOutputStream(fos))
        {
            oos.writeObject(empObj);
        }
    }

    // Deserialization code
    static Employee deserialize() throws IOException, ClassNotFoundException {
        try (FileInputStream fis = new FileInputStream("data.obj");
             ObjectInputStream ois = new ObjectInputStream(fis))
        {
            return (Employee) ois.readObject();
        }
    }
}

class Employee implements Serializable, ObjectInputValidation {
    private static final long serialVersionUID = 2L;

    private String name;
    private int age;

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // With ObjectInputValidation interface we get a validateObject method where we can do our validations.
    @Override
    public void validateObject() {
        System.out.println("Validating age.");

        if (age < 18 || age > 70)
        {
            throw new IllegalArgumentException("Not a valid age to create an employee");
        }
    }

    // Custom serialization logic,
    // This will allow us to have additional serialization logic on top of the default one e.g. encrypting object before serialization.
    private void writeObject(ObjectOutputStream oos) throws IOException {
        System.out.println("Custom serialization logic invoked.");
        oos.defaultWriteObject(); // Calling the default serialization logic
    }

    // Replacing de-serializing object with this,
    private Object writeReplace() throws ObjectStreamException {
        System.out.println("Replacing serialising object by this.");
        return this;
    }

    // Custom deserialization logic
    // This will allow us to have additional deserialization logic on top of the default one e.g. performing validations, decrypting object after deserialization.
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("Custom deserialization logic invoked.");

        ois.registerValidation(this, 0); // Registering validations, So our validateObject method can be called.

        ois.defaultReadObject(); // Calling the default deserialization logic.
    }

    // Replacing de-serializing object with this,
    // It will will not give us a full proof singleton but it will stop new object creation by deserialization.
    private Object readResolve() throws ObjectStreamException {
        System.out.println("Replacing de-serializing object by this.");
        return this;
    }

    @Override
    public String toString() {
        return String.format("Employee {name='%s', age='%s'}", name, age);
    }
}

You can find the complete source code for this article on this Github Repository and please feel free to provide your valuable feedback.
In my previous articles, I had explained the difference between deep and shallow cloning and how copy-constructors and defensive copy methods are better than default java cloning.

Java object cloning using copy constructors and defensive copy methods certainly have some advantages but we have to explicitly write some code to achieve deep cloning in all these approaches. And still, there are chances that we might miss something and do not get deeply cloned object.

And as discussed in 5 different ways to create objects in java, deserialising a serialised object creates a new object with the same state as in the serialized object. So similar to above cloning approaches we can achieve deep cloning functionality using object serialization and deserialization as well and with this approach we do not have worry about or write code for deep cloning, we get it by default.

However, cloning an object using serialization comes with some performance overhead and we can improve on it by using in-memory serialization if we just need to clone the object and don’t need to persist it in a file for future use.

We will use below Employee class as an example which has name, doj and skills as the state, for deep cloning, we do not need to worry about the name field because it is a String object and by default all strings are immutable in nature.

You can read more about immutability on How to Create an Immutable Class in Java and Why String is Immutable and Final.

class Employee implements Serializable {
    private static final long serialVersionUID = 2L;

    private String name;
    private LocalDate doj;
    private List<String> skills;

    public Employee(String name, LocalDate doj, List<String> skills) {
        this.name = name;
        this.doj = doj;
        this.skills = skills;
    }

    public String getName() { return name; }
    public LocalDate getDoj() { return doj; }
    public List<String> getSkills() { return skills; }

    // Method to deep clone a object using in memory serialization
    public Employee deepClone() throws IOException, ClassNotFoundException {
        // First serializing the object and its state to memory using ByteArrayOutputStream instead of FileOutputStream.
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
        out.writeObject(this);

        // And then deserializing it from memory using ByteArrayOutputStream instead of FileInputStream.
        // Deserialization process will create a new object with the same state as in the serialized object,
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream in = new ObjectInputStream(bis);
        return (Employee) in.readObject();
    }

    @Override
    public String toString() {
        return String.format("Employee{name='%s', doj=%s, skills=%s}", name, doj, skills);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Employee employee = (Employee) o;

        return Objects.equals(name, employee.name) &&
            Objects.equals(doj, employee.doj) &&
            Objects.equals(skills, employee.skills);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, doj, skills);
    }
}


To deep clone an object of Employee class, I have provided a deepClone() method which serializes the object to memory by using ByteArrayOutputStream instead of the FileOutputStream and deserialises it back using ByteArrayInputStream instead of the FileInputStream. Here we are serializing the object into bytes and deserializing it from bytes to object again.

How To Deep Clone An Object Using Java In Memory Serialization



Employee class implements Serializable interface to achieve serialization which has its own disadvantages and we can overcome some of these disadvantages by customizing the serialization process by using Externalizable interface.

We can run below tests to see if our cloning approach is deep or just shallow, here all == operations will return false (because both objects are separate) and all equals will return true (because both have the same content).

public static void main(String[] args) throws IOException, ClassNotFoundException {
 Employee emp = new Employee("Naresh Joshi", LocalDate.now(), Arrays.asList("Java", "Scala", "Spring"));
 System.out.println("Employee object: " + emp);

 // Deep cloning `emp` object by using our `deepClone` method.
 Employee clonedEmp = emp.deepClone();
 System.out.println("Cloned employee object: " + clonedEmp);

 System.out.println();

 // All of this will print false because both objects are separate.
 System.out.println(emp == clonedEmp);
 System.out.println(emp.getDoj() == clonedEmp.getDoj());
 System.out.println(emp.getSkills() == clonedEmp.getSkills());

 System.out.println();

 // All of this will print true because `clonedEmp` is a deep clone of `emp` and both have the same content.
 System.out.println(Objects.equals(emp, clonedEmp));
 System.out.println(Objects.equals(emp.getDoj(), clonedEmp.getDoj()));
 System.out.println(Objects.equals(emp.getSkills(), clonedEmp.getSkills()));
}

We know the deserialization process creates a new object every time which is not good if we have to make our class singleton. And that's why we need to override and disable serialization for our singleton class which we can achieve by providing writeReplace and readResolve methods.

Similar to serialization, Java cloning also does not play along with singleton pattern, and that's why we need to override and disable it as well. We can do that by implementing cloning in a way so that it will either throw CloneNotSupportedException or return the same instance every time.

You can read more about Java cloning and serialization on Java Cloning and Java Serialization topics.

You can find the complete source code for this article on this Github Repository and please feel free to provide your valuable feedback.
In a previous article Everything About Java Serialization Explained With Example, I explained how we can serialize/deserialize one object using Serializable interface and also explain how we can customise the serialization process using writeObject and readObject methods.

Disadvantages Of Java Serialization Process

But these customizations are not sufficient because JVM has full control of the serialization process and those customization logics are just additions to the default serialization process. We still have to use the default serialization logic by calling ObjectOutputStream.defaultWriteObject() and ObjectInputStream.defaultReadObject() from writeObject and readObject methods. And if do not call these default methods our object will not be serialized/deserialized.

The default serialization process is fully recursive. So whenever we try to serialize one object, the serialization process try to serialize all the fields (primitive and reference) with our class (except static and transient fields). Which makes serialization a very slow process.

Now let's assume we have an object with lots of fields which we do not want to serialize for some reasons (these fields will always be assigned with default values). With default serialization process we will have to make all these fields transient but it still will not be efficient because there will be a lot of checks to see if the fields are transient or not.

So as we can see there are lots of downside of the using default serialization process, like:
  1. Customizations to serialization are not sufficient because JVM has full control of the serialization process and our customization logics are just additions to the default serialization process.
  2. Default serialization process is fully recursive and slow.
  3. In order to not to serialize a field, we have to declare it transient and lots of transient fields will again make the process slow.
  4. We can not control how our fields will be serialized and deserialized.
  5. Default serialization process does not invoke constructors while creating the object so it can not call the initialization logic provided by the constructor.

What Is Externalization And Externalizable Interface


As we saw above that the default java serialization is not efficient. We can solve some of these issues by using Externalizable interface instead of Serializable interface.

We can write your own serialization logic by implementing the Externalizable interface and overriding it’s methods writeExternal() and readExternal(). But with this approach, we will not get any kind of default serialization logic from JVM and it is up to us to provide the complete serialization and deserialization logic.

So it is very necessary to code the test these methods very carefully because it might break the serialization process. But the externalization process is very fast in comparison to the default serialization process if implemented properly.

We will use below Employee class object as an example for the explanation:

// Using Externalizable, complete serialization/deserialization logic becomes our responsibility,
// We need to tell what to serialize using writeExternal() method and what to deserialize using readExternal(),
// We can even serialize/deserialize static and transient variables,
// With implementation of writeExternal() and readExternal(),  methods writeObject() and readObject() becomes redundant and they do not get called.
class Employee implements Externalizable {

   // This serialVersionUID field is necessary for Serializable as well as Externalizable to provide version control,
    // Compiler will provide this field if we do not provide it which might change if we modify class structure of our class, and we will get InvalidClassException,
    // If we provide a value to this field and do not change it, serialization-deserialization will not fail if we change our class structure.
    private static final long serialVersionUID = 2L;

    private String firstName;
    private transient String lastName; // Using Externalizable, we can even serialize/deserialize transient variables, so declaring fields transient becomes unnecessary.
    private int age;
    private static String department; // Using Externalizable, we can even serialize/deserialize static variables according to our need.


    // Mandatory to have to make our class Externalizable
    // When an Externalizable object is reconstructed, the object is created using public no-arg constructor before the readExternal method is called.
    // If a public no-arg constructor is not present then a InvalidClassException is thrown at runtime.
    public Employee() {
    }

    // All-arg constructor to create objects manually
    public Employee(String firstName, String lastName, int age, String department) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        Employee.department = department;

        validateAge();
    }

    private void validateAge() {
        System.out.println("Validating age.");

        if (age < 18 || age > 70) {
            throw new IllegalArgumentException("Not a valid age to create an employee");
        }
    }

    @Override
    // We need to tell what to serialize in writeExternal() method
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Custom externalizable serialization logic invoked.");

        out.writeUTF(firstName);
        out.writeUTF(lastName);
        out.writeInt(age);
        out.writeUTF(department);
    }

    @Override
    // We need to tell what to deserialize in readExternal() method
    // The readExternal method must read the values in the same sequence and with the same types as were written by writeExternal
    public void readExternal(ObjectInput in) throws IOException {
        System.out.println("Custom externalizable serialization logic invoked.");

        firstName = in.readUTF();
        lastName = in.readUTF();
        age = in.readInt();
        department = in.readUTF();

        validateAge();
    }

    @Override
    public String toString() {
        return String.format("Employee {firstName='%s', lastName='%s', age='%s', department='%s'}", firstName, lastName, age, department);
    }

    // Custom serialization logic, It will be called only if we have implemented Serializable instead of Externalizable.
    private void writeObject(ObjectOutputStream oos) throws IOException {
        System.out.println("Custom serialization logic invoked.");
    }

    // Custom deserialization logic, It will be called only if we have implemented Serializable instead of Externalizable.
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("Custom deserialization logic invoked.");
    }
}

How Serialization works with Externalizable Interface

As we can see above in our example Employee class, we can write your own serialization logic by implementing the Externalizable interface and overriding its methods writeExternal() and readExternal().

The object can implement the writeExternal method to save its contents by calling the methods of DataOutput for its primitive values or calling the writeObject method of ObjectOutput for objects, strings, and arrays.

The object can implement the readExternal method to restore its contents by calling the methods of DataInput for primitive types and readObject for objects, strings and arrays. The readExternal method must read the values in the same sequence and with the same types as were written by writeExternal.

// We need to tell what fields to serialize in writeExternal() method
public void writeExternal(ObjectOutput out) throws IOException {
    System.out.println("Custom externalizable serialization logic invoked.");

    out.writeUTF(firstName);
    out.writeUTF(lastName);
    out.writeInt(age);
    out.writeUTF(department);
}

// We need to tell what fields to deserialize in readExternal() method
// The readExternal method must read the values in the same sequence and with the same types as were written by writeExternal
public void readExternal(ObjectInput in) throws IOException {
    System.out.println("Custom externalizable serialization logic invoked.");

    firstName = in.readUTF();
    lastName = in.readUTF();
    age = in.readInt();
    department = in.readUTF();

    validateAge();
}

To serialize and deserialize our object to a file we need to follow the same procedure as we followed in the Serializable example which means calling ObjectOutputStream.writeObject() and ObjectInputStream.readObject() as done in the following code:

public class ExternalizableExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Employee empObj = new Employee("Shanti", "Sharma", 25, "IT");
        System.out.println("Object before serialization  => " + empObj.toString());

        // Serialization
        serialize(empObj);

        // Deserialization
        Employee deserializedEmpObj = deserialize();
        System.out.println("Object after deserialization => " + deserializedEmpObj.toString());
    }

    // Serialization code
    static void serialize(Employee empObj) throws IOException {
        try (FileOutputStream fos = new FileOutputStream("data.obj");
             ObjectOutputStream oos = new ObjectOutputStream(fos))
        {
            oos.writeObject(empObj);
        }
    }

    // Deserialization code
    static Employee deserialize() throws IOException, ClassNotFoundException {
        try (FileInputStream fis = new FileInputStream("data.obj");
             ObjectInputStream ois = new ObjectInputStream(fis))
        {
            return (Employee) ois.readObject();
        }
    }
}

The Externalizable interface is a child interface of Serializable i.e. Externalizable extends Serializable. So if we implement Externalizable interface and override its writeExternal() and readExternal() methods then first preference is given to these methods over the default serialization mechanism provided by JVM. These methods supersede customized implementations of writeObject and readObject methods, So if we also provide writeObject() and readObject() then they will be ignored.

In the serialization process, each object to be serialized is tested for the Externalizable interface. If the object supports Externalizable, the writeExternal method is called. If the object does not support Externalizable and does implement Serializable, the object is saved using ObjectOutputStream.

When an Externalizable object is reconstructed, an instance is created using the public no-arg constructor, then the readExternal method called. Serializable objects are restored by reading them from an ObjectInputStream.

  1. When an Externizable object is reconstructed, and object is created using public no-arg constructor before the readExternal method is called. If a public no-arg constructor is not present then a InvalidClassException is thrown at runtime.
  2. Using Externalizable, we can even serialize/deserialize transient variables, so declaring fields transient becomes unnecessary.
  3. Using Externalizable, we can even serialize/deserialize static variables if we need to.

An Externalizable instance can designate a substitution object via the writeReplace and readResolve methods documented in the Serializable interface.

Java serialization can also be used to deep clone an object. Java cloning is the most debatable topic in Java community and it surely does have its drawbacks but it is still the most popular and easy way of creating a copy of an object until that object is full filling mandatory conditions of Java cloning. I have covered cloning in details in a 3 article long Java Cloning Series which includes articles like Java Cloning And Types Of Cloning (Shallow And Deep) In Details With Example, Java Cloning - Copy Constructor Versus Cloning, Java Cloning - Even Copy Constructors Are Not Sufficient, go ahead and read them if you want to know more about cloning.

Differences between Externalizable vs Serializable

Let’s list down the main differences between Externalizable and Serializable interfaces in java.




You can find the complete source code for this article on this Github Repository and please feel free to provide your valuable feedback.
In a previous article, we looked at 5 different ways to create objects in java, I have explained how deserialising a serialised object creates a new object and in this blog, I am going to discuss Serialization and Deserialization in details.

We will use below Employee class object as an example for the explanation

// If we use Serializable interface, static and transient variables do not get serialize
class Employee implements Serializable {

    // This serialVersionUID field is necessary for Serializable as well as Externalizable to provide version control,
    // Compiler will provide this field if we do not provide it which might change if we modify the class structure of our class, and we will get InvalidClassException,
    // If we provide value to this field and do not change it, serialization-deserialization will not fail if we change our class structure.
    private static final long serialVersionUID = 2L;

    private final String firstName; // Serialization process do not invoke the constructor but it can assign values to final fields
    private transient String middleName; // transient variables will not be serialized, serialised object holds null
    private String lastName;
    private int age;
    private static String department; // static variables will not be serialized, serialised object holds null

    public Employee(String firstName, String middleName, String lastName, int age, String department) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
        this.age = age;
        Employee.department = department;

        validateAge();
    }

    private void validateAge() {
        System.out.println("Validating age.");

        if (age < 18 || age > 70) {
            throw new IllegalArgumentException("Not a valid age to create an employee");
        }
    }

    @Override
    public String toString() {
        return String.format("Employee {firstName='%s', middleName='%s', lastName='%s', age='%s', department='%s'}", firstName, middleName, lastName, age, department);
    }

  // Custom serialization logic,
    // This will allow us to have additional serialization logic on top of the default one e.g. encrypting object before serialization
    private void writeObject(ObjectOutputStream oos) throws IOException {
        System.out.println("Custom serialization logic invoked.");
        oos.defaultWriteObject(); // Calling the default serialization logic
    }

    // Custom deserialization logic
    // This will allow us to have additional deserialization logic on top of the default one e.g. decrypting object after deserialization
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("Custom deserialization logic invoked.");

        ois.defaultReadObject(); // Calling the default deserialization logic

        // Age validation is just an example but there might some scenario where we might need to write some custom deserialization logic
        validateAge();
    }

}

What are Serialization and Deserialization

In Java, we create several objects which live and die accordingly and every object will certainly die when the JVM dies but sometimes we might want to reuse an object between several JVMs or we might want to transfer an object to another machine over the network.

Well, serialization allows us to convert the state of an object into a byte stream, which then can be saved into a file on the local disk or sent over the network to any other machine. And deserialization allows us to reverse the process, which means reconverting the serialized byte stream to an object again.

In simple words, object serialization is the process of saving an object's state to a sequence of bytes and deserialization is the process of reconstructing an object from those bytes. Generally, the complete process is called serialization but I think it is better to classify both as separate for more clarity.

The serialization process is platform independent, an object serialized on one platform can be deserialized on a different platform.

To serialize and deserialize our object to a file we need to call ObjectOutputStream.writeObject() and ObjectInputStream.readObject() as done in the following code:

public class SerializationExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Employee empObj = new Employee("Shanti", "Prasad", "Sharma", 25, "IT");
        System.out.println("Object before serialization  => " + empObj.toString());

        // Serialization
        serialize(empObj);

        // Deserialization
        Employee deserialisedEmpObj = deserialize();
        System.out.println("Object after deserialization => " + deserialisedEmpObj.toString());
    }

    // Serialization code
    static void serialize(Employee empObj) throws IOException {
        try (FileOutputStream fos = new FileOutputStream("data.obj");
             ObjectOutputStream oos = new ObjectOutputStream(fos))
        {
            oos.writeObject(empObj);
        }
    }

    // Deserialization code
    static Employee deserialize() throws IOException, ClassNotFoundException {
        try (FileInputStream fis = new FileInputStream("data.obj");
             ObjectInputStream ois = new ObjectInputStream(fis))
        {
            return (Employee) ois.readObject();
        }
    }
}

Only classes which implement Serializable can be serialized

Similar to the Cloneable interface for Java cloning in serialization, we have one marker interface Serializable which works like a flag for the JVM. Any class which implements Serializable interface directly or through its parent can be serialised and classes which do not implement Serializable can not be serialized.

Java’s default serialization process is fully recursive, so whenever we try to serialize one object, the serialization process try to serialize all the fields (primitive and reference) with our class (except static and transient fields).

When a class implements theSerializable interface, all its sub-classes are serializable as well. But when an object has a reference to another object, these objects must implement the Serializable interface separately. If our class is having even a single reference to a non Serializable class then JVM will throw NotSerializableException.

Why Serializable is not implemented by Object?

Now a question arises if Serialization is very basic functionality and any class which do not implement Serializable can not be serialised, then why Serializable is not implemented by the Object itself?, By this way, all our objects could be serialized by default.

The Object class does not implement Serializable interface because we may not want to serialize all the objects e.g. serialising a thread does not make any sense because thread running in my JVM would be using my system's memory, persisting it and trying to run it in your JVM would make no sense.

The transient and static fields do not get serialized

If we want to serialize one object but do not want to serialize some specific fields then we can mark those fields as transient.

All the static fields belong to the class instead of the object, and the serialization process serialises the object so static fields can not be serialized.

  1. Serialization does not care about access modifiers of the field such as private. All non transient and non static fields are considered part of an object's persistent state and are eligible for serialisation.
  2. We can assign values to final fields in conscrutors only and serialization process do not invoke any constructor but still it can assign values to final fields.

What is serialVersionUID and Why should we declare it?

Suppose we have a class and we have serialized its object to a file on the disk, and due to some new requirements, we added/removed one field from our class. Now if we try to deserialize the already serialized object we will get InvalidClassException, why?

We get it because by default JVM associates a version number to each serializable class to control the class versioning. It is used to verify that the serialized and deserialized objects have the same attributes and thus are compatible with deserialization. The version number is maintained in a field called serialVersionUID. If a serializable class doesn’t declare a serialVersionUID JVM will generate one automatically at run-time.

If we change our class structure e.g. remove/add fields that version number also changes and according to JVM our class is not compatible with the class version of the serialized object. That's why we get the exception but if you really think about it, why should it be thrown just because I added a field? Couldn't the field just be set to its default value and then written out next time?

Yes, it can be done by providing the serialVersionUID field manually and ensure it is always the same. It is highly recommended that each serializable class declares its serialVersionUID as the generated one is compiler dependent and thus may result in unexpected InvalidClassExceptions.


You can use a utility that comes with the JDK distribution called serialver to see what that code would be by default (it is just the hash code of the object by default).

Customizing Serialization and Deserialization with writeObject and readObject methods

JVM has full control for serializing the object in the default serialization process but there are lots of downside of the using default serialization process, some of which are:

  1. It can not handle serialisation of fields which are not serializable.
  2. Deserialization process does not invoke constructors while creating the object so it can not call the initialization logic provided by the constructor.

But we can override this the default serialization behaviour inside our Java class and provide some additional logic to enhance the normal process. This can be done by providing two methods writeObject and readObject inside the class that we want to serialize:

// Custom serialization logic will allow us to have additional serialization logic on top of the default one e.g. encrypting object before serialization
private void writeObject(ObjectOutputStream oos) throws IOException {
  // Any Custom logic
 oos.defaultWriteObject(); // Calling the default serialization logic
  // Any Custom logic
}

// Custom deserialization logic will allow us to have additional deserialization logic on top of the default one e.g. decrypting object after deserialization
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
 // Any Custom logic
 ois.defaultReadObject(); // Calling the default deserialization logic
  // Any Custom logic
}

Declaring both methods as private is necessary (public methods will not work) so rather than JVM nothing else can see them. This also proves that neither method is not inherited nor overridden or overloaded. JVM automatically checks these methods and call them during the serialization-deserialization process. JVM can call these private methods but other objects can not thus, the integrity of the class is maintained and the serialization protocol can continue to work as normal.

Even though those specialized private methods are provided, the object serialization works the same way by calling ObjectOutputStream.writeObject() or ObjectInputStream.readObject().

The call to ObjectOutputStream.writeObject() or ObjectInputStream.readObject() kicks off the serialization protocol. First, the object is checked to ensure it implements Serializable and then it is checked to see whether either of those private methods is provided. If they are provided, the stream class is passed as the parameter to these methods, giving the code control over its usage.

We can call ObjectOutputStream.defaultWriteObject() and ObjectInputStream.defaultReadObject() from these methods to gain default serialization logic. Those calls do what they sound like -- they perform the default writing and reading of the serialized object, which is important because we are not replacing the normal process, we are only adding to it.

Those private methods can be used for any customization you want to make in the serialization process, e.g. encryption can be added to the output and decryption to the input (note that the bytes are written and read in cleartext with no obfuscation at all). They could be used to add extra data to the stream, perhaps a company versioning code, the possibilities are truly limitless.

Apart from writeObject and readObject methods, we can also leverage other serialization magic methods to get some additional functionality.

Stopping Serialization and Deserialization

Suppose we have a class which got the serialization capability from its parent, which means our class extends from another class which implements Serializable.

It means anybody can serialize and deserialize the object of our class. But what if we do not want our class to be serialized or deserialized e.g. our class is a singleton and we want to prevent any new object creation, remember the deserialization process creates a new object.

To stop the serialization for our class, we can once again use the above private methods to just throw the NotSerializableException. Any attempt to serialize or deserialise our object will now always result in the exception being thrown. And since those methods are declared as private, nobody can override your methods and change them.

private void writeObject(ObjectOutputStream oos) throws IOException {
  throw new NotSerializableException("Serialization is not supported on this object!");
}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  throw new NotSerializableException("Serialization is not supported on this object!");
}


However, this is a violation of the Liskov Substitution Principle. And writeReplace and readResolve methods can be used to achieve singleton like behaviours. These methods are used to allow an object to provide an alternative representation for itself within an ObjectStream. In simple words, readResolve can be used to change the data that is deserialized through the readObject method and writeReplace can be used to change the data that is serialized through writeObject.

Java serialization can also be used to deep clone an object. Java cloning is the most debatable topic in Java community and it surely does have its drawbacks but it is still the most popular and easy way of creating a copy of an object until that object is full filling mandatory conditions of Java cloning. I have covered cloning in details in a 3 article long Java Cloning Series which includes articles like Java Cloning And Types Of Cloning (Shallow And Deep) In Details With Example, Java Cloning - Copy Constructor Versus Cloning, Java Cloning - Even Copy Constructors Are Not Sufficient, go ahead and read them if you want to know more about cloning.

Conclusion

  1. Serialization is the process of saving an object's state to a sequence of bytes which then can be stored on a file or sent over the network and deserialization is the process of reconstructing an object from those bytes.
  2. Only subclasses of the Serializable interface can be serialized.
  3. If our class does not implement Serializable interface or if it is having a reference to a non Serializable class then JVM will throw NotSerializableException.
  4. All transient and static fields do not get serialized.
  5. The serialVersionUID is used to verify that the serialized and deserialized objects have the same attributes and thus are compatible with deserialization.
  6. We should create a serialVersionUID field in our class so if we change our class structure (adding/removing fields) JVM will not through InvalidClassException. If we do not provide it JVM provides one which might change when our class structure changes.
  7. We can override the default serialization behaviour inside our Java class by providing the implementation of writeObject and readObject methods.
  8. And we can call ObjectOutputStream.defaultWriteObject() and ObjectInputStream.defaultReadObject from writeObject and readObject methods to get the default serialization and deserialization logic.
  9. We can throw NotSerializableException exception from writeObject and readObject , if we do not want our class to be serialized or deserialized.
Java Serialization process can be further customized and enhanced using the Externalizable interface which I have explained in How to Customize Serialization In Java By Using Externalizable Interface.

I have also written a series of articles explaining item numbers 74 to 78 of Effective Java, which further discusses how Java serialization process can be enhanced, please go ahead and read them if you like.

You can find the complete source code for this article on this Github Repository and please feel free to provide your valuable feedback.

Some time back I have written one article Java Lambda Expression Explained with Example but it was easy for me to explore and Java 8 because I was using it in my project and I was allowed to install and use it. But in my current project, we are still using Java 8 and now I want to upgrade myself and learn Java 11 but can not install it.

And this happens with a lot of Java developers, Sometimes you want to learn and explore the latest version of Java e.g. Java 11 but you can not install it on your machine because you and your team is working on some older version e.g. Java 8, and you do not want to break your project.

Or suppose you are working on multiple projects some of which use Java 8 and some new projects are using Java 11. So in order to work on these projects parallelly, you will need to install multiple JDK on your machine and you should be able to switch between them.

What if there is a way and what if you are enabled to install multiple versions of Java and turn off and turn on them according to your need.




Well there is a tool called SDKMan which allow us to do the same and according to the official website:

SDKMan! is a tool for managing parallel versions of multiple Software Development Kits on most Unix based systems. It provides a convenient Command Line Interface (CLI) and API for installing, switching, removing and listing Candidates.

Some points about SDKMan are as following:
  1. SDKMan is free to use and it is developed by the open-source community.
  2. SDKMan is written in bash and it only requires curl and zip/unzip programs to be present on your system.
  3. SDKMan can install around 29 Software Development Kits for the JVM such as Java, Groovy, Scala, Kotlin and Ceylon. Ant, Gradle, Grails, Maven, SBT, Spark, Spring Boot, Vert.x.
  4. We do not need to worry about setting the _HOME and PATH environment variables because SDKMan handles it automatically.

Installing SDKMan

SDKMan can run on any UNIX based platforms such as Mac OSX, Linux, Cygwin, Solaris and FreeBSD and we can install it using following commands:

Simply open a new terminal and enter:

$ curl -s "https://get.sdkman.io" | bash
$ source "$HOME/.sdkman/bin/sdkman-init.sh"

We can verify the installation using sdk version and sdk help command will us complete help about the usage of sdk command.

Because SDKMan is written in bash and only requires curl and zip/unzip to be present on your system. You can install SDKMan on windows as well either by first installing Cygwin or Git Bash for Windows environment and then running above commands.

Installing Java Using SDKMan

SDKMan supports installations of 29 Software Development Kits for the JVM e.g. Java, Groovy, Scala, Kotlin and Ceylon, Grails, SBT, Spark, Spring Boot. We can get the complete list using sdk list command.

SDKMan also helps us install build tools such as Ant, Maven and Gradle, You can read more about these build tools on Java Build Tools Comparisons: Ant vs Maven vs Gradle.

Command sdk list java will give us a list of java versions which we can install using SDKMan like below:

$ sdk list java

================================================================================
Available Java Versions
================================================================================
     13.ea.02-open       1.0.0-rc-10-grl                                        
     12.ea.26-open       1.0.0-rc-9-grl                                         
     11.0.2-zulu         1.0.0-rc-8-grl                                         
     11.0.2-open                                                                
     11.0.1-zulufx                                                              
     10.0.2-zulu                                                                
     10.0.2-open                                                                
     9.0.7-zulu                                                                 
     9.0.4-open                                                                 
     8.0.202-amzn                                                               
     8.0.201-zulu                                                               
     8.0.201-oracle                                                             
     8.0.192-zulufx                                                             
     7.0.181-zulu                                                               
     1.0.0-rc-11-grl                                                            
================================================================================
+ - local version
* - installed
> - currently in use
================================================================================

Now Suppose we want to install OpenJDK version of Java v9 then we will execute below command in our terminal which will also update the PATH and JAVA_HOME variables:

$ sdk install java 9.0.4-open

The command will take some time to execute because it will be downloading the JDK on our machine and the command will output something like:

Downloading: java 9.0.4-open
In progress...
######################################################################## 100.0%

Repackaging Java 9.0.4-open...

Done repackaging...
Cleaning up residual files...

Installing: java 9.0.4-open
Done installing!

Setting java 9.0.4-open as default.


Now if we check our Java version and JAVA_HOME, we can see Java has been updated to 9.0.4

$ java -version
openjdk version "9.0.4"
OpenJDK Runtime Environment (build 9.0.4+11)
OpenJDK 64-Bit Server VM (build 9.0.4+11, mixed mode)

$ echo $JAVA_HOME
/Users/xbbnrde/.sdkman/candidates/java/current

We can follow the same process to install any version which we can see in the above list, let us suppose we want to install Java V11 version then we can execute the command:

$ sdk install java 11.0.2-open
Downloading: java 11.0.2-open
In progress...
######################################################################## 100.0%

Repackaging Java 11.0.2-open...

Done repackaging...
Cleaning up residual files...

Installing: java 11.0.2-open
Done installing!

Do you want java 11.0.2-open to be set as default? (Y/n): Y

Setting java 11.0.2-open as default.

Now if we again check our Java version and JAVA_HOME, we can see Java has been updated to 11.0.2:

$ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)

$ echo $JAVA_HOME
/Users/xbbnrde/.sdkman/candidates/java/current

Note: Some times you will require to close and reopen your terminal in order to see the changes.
sdkman-installing-and-running-java11-and-java-9-simultaneously

Pointing SDKMan to your existing installed Java version

First, we need to find out where on your machine Java is installed. On my machine, it is installed in the folder jdk1.8.0_172.jdk which lies under the folder /Library/Java/JavaVirtualMachines, let's refer to this folder by <java-folder>.

The second thing we want to do is to set up a symbolic link between our installed <java-folder> and SDKMan, we can do it by running below commands:

$ ln -s /Library/Java/JavaVirtualMachines/<java-folder>  ~/.sdkman/candidates/java/<java-folder>

$ sudo ln -s /Library/Java/JavaVirtualMachin
es/<java-folder>/Contents/Home/bin  ~/.sdkman/candidates/java/<java-folder>/bin


Now if we again execute sdk list java command, we will get:

================================================================================
Available Java Versions
================================================================================
   + jdk1.8.0_172.jd     8.0.201-zulu                                           
     13.ea.02-open       8.0.201-oracle                                         
     12.ea.26-open       8.0.192-zulufx                                         
     11.0.2-zulu         7.0.181-zulu                                           
 > * 11.0.2-open         1.0.0-rc-12-grl                                        
     11.0.2.j9-adpt      1.0.0-rc-11-grl                                        
     11.0.2.hs-adpt      1.0.0-rc-10-grl                                        
     11.0.1-zulufx       1.0.0-rc-9-grl                                         
     10.0.2-zulu         1.0.0-rc-8-grl                                         
     10.0.2-open                                                                
     9.0.7-zulu                                                                 
   * 9.0.4-open                                                                 
     8.0.202-amzn                                                               
     8.0.202.j9-adpt                                                            
     8.0.202.hs-adpt                                                            

================================================================================
+ - local version
* - installed
> - currently in use
================================================================================


We can clearly see that we have 3 JDK installed on our machine and JDK 11.0.2-open is in use as of now. However, if we want to switch between them or install a new one we can do that very easily by using sdk use java <version_which_you_want_to_use> like the following:

Using Java 9

$ sdk use java 9.0.4-open
Using java version 9.0.4-open in this shell.

$ java -version
openjdk version "9.0.4"
OpenJDK Runtime Environment (build 9.0.4+11)
OpenJDK 64-Bit Server VM (build 9.0.4+11, mixed mode)

Using locally installed Java 8

$ sdk use java jdk1.8.0_172.jdk
Using java version jdk1.8.0_172.jdk in this shell.
Add caption
$ java -version java version "1.8.0_172" Java(TM) SE Runtime Environment (build 1.8.0_172-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.172-b11, mixed mode)

Using Java 11

$ sdk use java 11.0.2-open
Using java version 11.0.2-open in this shell.

$ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)


Using sdk use command will only enable you to use a specified version of JDK in the same terminal shell where you are executing the sdk use command. And if you close the terminal and open it again you will be on the previously installed version.

But if you want to activate one version of JDK for all terminals and applications, you can use the command sdk default java <your_version>

$ sdk default java 11.0.2-open
Default java version set to 11.0.2-open

$ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
Or you can uninstall that version and install it again like below which will set that version to the current JDK.

Uninstalling a JDK Version

In case you want to uninstall any JDK version e.g., 11.0.2-open you can do that as follows:

$ sdk uninstall java 11.0.2-open
And in case you want to install the uninstalled version back again, you can install it again and this time SDKMan will not download it because it already has that on your machine (unless you do not delete that manually).

$ sdk install java 11.0.2-open

Found a previously downloaded java 11.0.2-open archive. Not downloading it again...

Installing: java 11.0.2-open
Done installing!

Do you want java 11.0.2-open to be set as default? (Y/n): y

Setting java 11.0.2-open as default.

Using SDMan installed JDK from IntelliJ IDEA

SDKMan installs all JDK under .sdkman/candidates/java/ folder and you can find .sdkman folder in your home directory.

After opening any Java project in IntelliJ, you can press Command + : to open the project structure window. In that window, you will need to click on New... under Project SDK section and there you can enter .sdkman/candidates/java/ in the browse window and can select any JDK from .sdkman/candidates/java/.

Because .sdkman is a hidden folder and in case you face some problem in finding it, you can always create a symbolic link to it with a non-hidden folder using below command.

$ ln -s ~/.sdkman ~/sdkman

You can find the complete source code for my articles on this Github Repository and please feel free to provide your valuable feedback.

In an interview, one of my friends was asked that if we have two Integer objects, Integer a = 127; Integer b = 127; Why a == b evaluate to true when both are holding two separate objects? In this article, I will try to answer this question and also try to explain the answer.

Short Answer

The short answer to this question is, direct assignment of an int literal to an Integer reference is an example of auto-boxing concept where the literal value to object conversion code is handled by the compiler, so during compilation phase compiler converts Integer a = 127; to Integer a = Integer.valueOf(127);.

The Integer class maintains an internal IntegerCache for integers which by default ranges from -128 to 127 and Integer.valueOf() method returns objects of mentioned range from that cache. So a == b returns true because a and b both are pointing to the same object.


Long Answer

In order to understand the short answer let's first understand the Java types, all types in Java lies under two categories

  1. Primitive Types: There are 8 primitive types (byte, short, int, long, float, double, char and boolean) in Java which holds their values directly in the form of binary bits.
    For example, int a = 5; int b = 5; here a and b directly holds the binary value of 5 and if we try to compare a and b using a == b we are actually comparing 5 == 5 which returns true.

  2. Reference Types: All types other than primitive types lies under the category of reference types e.g. Classes, Interfaces, Enums, Arrays etc. and reference types holds the address of the object instead of the object itself.
    For example, Integer a = new Integer(5); Integer b = new Integer(5), here a and b do not hold the binary value of 5 instead a and b holds memory addresses of two separate objects where both objects contain a value 5. So if we try to compare a and b using a == b, we are actually comparing those two separate memory addresses hence we get false, to perform actual equality on a and b we need to perform a.euqals(b).

    Reference types are further divided into 4 categories Strong, Soft, Weak and Phantom References.

And we know that Java provides wrapper classes for all primitive types and support auto-boxing and auto-unboxing.

// Example of auto-boxing, here c is a reference type
Integer c = 128; // Compiler converts this line to Integer c = Integer.valueOf(128); 

// Example of auto-unboxing, here e is a primitive type
int e = c; // Compiler converts this line to int e = c.intValue();

Now if we create two integer objects a and b, and try to compare them using the equality operator ==, we will get false because both references are holding different-different objects

Integer a = 128; // Compiler converts this line to Integer a = Integer.valueOf(128);
Integer b = 128; // Compiler converts this line to Integer b = Integer.valueOf(128);

System.out.println(a == b); // Output -- false

But if we assign the value 127 to both a and b and try to compare them using the equality operator ==, we will get true why?

Integer a = 127; // Compiler converts this line to Integer a = Integer.valueOf(127);
Integer b = 127; // Compiler converts this line to Integer b = Integer.valueOf(127);

System.out.println(a == b); // Output -- true

As we can see in the code that we are assigning different objects to a and b but a == b can return true only if both a and b are pointing to the same object.

So how the comparison returning true? what's actually happening here? are a and b pointing to the same object?

Well till now we know that the code Integer a = 127; is an example of auto-boxing and compiler automatically converts this line to Integer a = Integer.valueOf(127);.

So it is the Integer.valueOf() method which is returning these integer objects which means this method must be doing something under the hood.

And if we take a look at the source code of Integer.valueOf() method, we can clearly see that if the passed int literal i is greater than IntegerCache.low and less than IntegerCache.high then the method returns Integer objects from IntegerCache. Default values for IntegerCache.low and IntegerCache.high are -128 and 127 respectively.

In other words, instead of creating and returning new integer objects, Integer.valueOf() method returns Integer objects from an internal IntegerCache if the passed int literal is greater than -128 and less than 127.

/**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
 public static Integer valueOf(int i) {
     if (i >= IntegerCache.low && i <= IntegerCache.high)
         return IntegerCache.cache[i + (-IntegerCache.low)];
     return new Integer(i);
 }

Java caches integer objects which fall into -128 to 127 range because this range of integers gets used a lot in day to day programming which indirectly saves some memory.

As you can see in the following image Integer class maintains an inner static IntegerCache class which acts as the cache and holds integer objects from -128 to 127 and that's why when we try to get integer object for 127 we always get the same object.

integer-cache-source-code


The cache is initialized on the first usage when the class gets loaded into memory because of the static block. The max range of the cache can be controlled by the -XX:AutoBoxCacheMax JVM option.

This caching behavior is not applicable to Integer objects only, similar to Integer.IntegerCache we also have ByteCache, ShortCache, LongCache, CharacterCache for Byte, Short, Long, Character respectively.

Byte, Short and Long have a fixed range for caching between –127 to 127 (inclusive) but for Character, the range is from 0 to 127 (inclusive). The range can be modified via argument only for Integer but not for others.

You can find the complete source code for this article on this Github Repository and please feel free to provide your valuable feedback.
When we create a variable in both parent and child class with the same name, and try to access it using parent's class reference which is holding a child class's object then what do we get?

In order to understand this, let us consider below example where we declare a variable x with the same name in both Parent and Child classes.

class Parent {
    // Declaring instance variable by name `x`
    String x = "Parent`s Instance Variable";

    public void print() {
        System.out.println(x);
    }
}

class Child extends Parent {

    // Hiding Parent class's variable `x` by defining a variable in child class with same name.
    String x = "Child`s Instance Variable";

    @Override
    public void print() {
        System.out.print(x);

        // If we still want to access variable from super class, we do that by using `super.x`
        System.out.print(", " + super.x + "\n");
    }
}


And now if we try to access x using below code, what System.out.println(parent.x) will print


Parent parent = new Child();
System.out.println(parent.x) // Output -- Parent`s Instance Variable


Well generally, we will say Child class will override the variable declared in the Parent class and parent.x will give us whatever Child's object is holding. Because it is the same thing which happens while we do same kind of operation on methods.

But actually it is not, and parent.x will give us value Parent`s Instance Variable which is declared in Parent class but why?

Because variables in Java do not follow polymorphism and overriding is only applicable to methods but not to variables. And when an instance variable in a child class has the same name as an instance variable in a parent class, then the instance variable is chosen from the reference type.

In Java, when we define a variable in Child class with a name which we have already used to define a variable in the Parent class, Child class's variable hides parent's variable, even if their types are different. And this concept is known as Variable Hiding.

In other words, when the child and parent class both have a variable with the same name, Child class's variable hides the parent class's variable. You can read more on variable hiding in the article What is Variable Shadowing and Hiding in Java.





Variable Hiding is not the same as Method Overriding

While variable hiding looks like overriding a variable similar to method overriding but it is not, overriding is applicable only to methods while hiding is applicable to variables.

In the case of method overriding, overriding methods completely replaces the inherited methods so when we try to access the method from parent's reference by holding child's object, the method from child class gets called. You can read more about overriding and how overridden methods completely replace the inherited methods on Everything About Method Overloading Vs Method OverridingWhy We Should Follow Method Overriding Rules.

But in variable hiding child class hides the inherited variables instead of replacing which basically means is that the object of Child class contains both variables but Child's variable hides Parent's variable. so when we try to access the variable from within Child class, it will be accessed from the child class.

And if I simplify section Example 8.3.1.1-3. Hiding of Instance Variables of Java language specification:

When we declare a variable in a Child class which has the same name e.g. x as an instance variable in a Parent class then
  1. Child class's object contains both variables (one inherited from Parent class and other declared in Child itself) but child class variable hides parent class's variable.
  2. Because the declaration of x in class Child hides the definition of x in class Parent, within the declaration of class Child, the simple name x always refers to the field declared within class Child. And if code in methods of Child class want to refer to the variable x of Parent class then this can be done as super.x.
  3. If we are trying to access the variable outside of Parent and Child class, then the instance variable is chosen from the reference type. Thus, the expression parent2.x in following code gives the variable value which belongs to parent class even if it is holding the object of the Child but ((Child) parent2).x accesses the value from the Child class because we casted the same reference to Child.
Why Instance Variable Of Super Class Is Not Overridden In Sub Class due to variable shadowing

Why Variable Hiding Is Designed This Way

So we know that instance variables are chosen from the reference type, not instance type, and polymorphism is not applicable to variables but the real question is why? why variables are designed to follow hiding instead of overriding.

Because variable overriding might break methods inherited from the parent if we change its type in the child class.

We know every child class inherits variables and methods (state and behaviour) from its parent class. Imagine if Java allows variable overriding and we change the type of a variable from int to Object in the child class. It will break any method which is using that variable and because the child has inherited those methods from the parent, the compiler will give errors in child class.

For example:

class Parent {
    int x;
    public int increment() {
        return ++x;
    }
    public int getX() {
        return x;
    }
}

class Child extends Parent {
    Object x;
    // Child is inherting increment(), getX() from Parent and both methods returns an int 
    // But in child class type of x is Object, so increment(), getX() will fail to compile. 
}

If Child.x overrides Parent.x, how can increment() and getX() work? In the subclass, these methods will try to return a value of a field of the wrong type!

And as mentioned, if Java allows variable overriding then Child's variable cannot substitute Parent's variable and this would break the Liskov Substitutability Principle (LSP).

Why Instance Variable Is Chosen from Reference Type Instead Of Instance

As explained in How Does JVM Handle Method Overloading and Overriding Internally,  at compile time overriding method calls are treated from the reference class only but all overridden methods get replaced by the overriding method at the runtime using a vtable and this phenomenon is called runtime polymorphism.

Similarly, at compile time variable access is also treated from the reference type but as we discussed variables do not follow overriding or runtime polymorphism, so they are not replaced by child class variables at the runtime and still refer to the reference type.


Generally speaking, nobody will ever recommend hiding fields as it makes code difficult to read and creates confusion. This kind of confusion will not there if we always stick to General Guidelines to create POJOs and encapsulate our fields by declaring them as private and provides getters/setters as required so the variables are not visible outside that class and child class cannot access them.

You can find complete code on this Github Repository and please feel free to provide your valuable feedback.
In order to create an object, we need to define a class that's why the class is called the blueprint of the object and an immutable class is a class which we can use to create immutable objects.

What is an immutable object

An object is called immutable if its state cannot be modified by anyone in any way after its construction, here object's state means the fields or the variables it is holding.

An immutable object does not expose its state to the outer world and neither provides any behaviour to modify its state. All wrapper classes i.e IntegerFloatLong are immutable in nature and other examples of immutable classes are String, java.util.UUID, java.net.URL.

Advantages of immutable objects

In Why String is Immutable and Final in Java, I have discussed how just by being immutable in nature, String gains lots of advantages including
  1. Thread Safety, 
  2. Hash-code caching,
  3. Object pool,
  4. Security.
Same advantages are applied to other immutable objects as well.

How to create a class for an immutable object

To create an immutable object we need to define our class in a way that it restricts every one (including itself) from changing the state of the object after its construction, and in order to do so we need to
  1. Mark your class final,
  2. Mark all the fields private,
  3. Mark all fields final as well,
  4. Provide an argument constructor with all initialization logic,
  5. Initialize all mutable fields by deep copying,
  6. Do not provide setters for your fields,
  7. Return a deep copy of mutable fields from the getters.
How-to-Create-an-Immutable-Class-in-Java-with-Example

Let's look at all these rules and the reasons to follow them

1. Why mark our class final

We should declare our class final to forbids its extension so no one can extend our class and destroy its immutability. If it is not final then in future someone might extend it and modify the behaviour to change the state.

2. Why mark all the fields private

We should mark all the fields private so no one can access them outside of the class.

3. Why mark all fields final as well

Mark all the fields final so even we will not be able to change the fields outside of the constructor.

4. Why provide an argument constructor with all initialization logic

A constructor is a place to write our object initialization logic because constructor gets called whenever we create an object.

So when we want to set our object's state during object creation only, we need to set it in the constructor and that's why we need to have an argument constructor in case of an immutable class.

As discussed in 5 different ways to create objects and creating objects through reflection, serialization and cloning also create new objects but both of them does not include a constructor call. But we do not need to worry about it because in both ways object will be constructed from an already present object which will be already immutable in nature.

5. Why initialize all mutable fields by deep copying

If our immutable object holds a reference to other immutable objects i.e. String, Integer we do not need to worry because we know they will not allow any change in their state.

But if our object holds references to some mutable objects and those mutable objects are also getting referred from somewhere else, in that case, our object's immutability is in danger.

In our example, our ImmutableEmployee class holds a reference to Date class which is mutable in nature. In below lines of code we are creating a variable dob which is holding a Date object and then we are passing it to ImmutableEmployee's constructor and creating an object which is being referred from employee.

Date dob = new Date();
ImmutableEmployee employee = new ImmutableEmployee(1, "Naresh", dob);

When we SysOut employee object we will get

ImmutableEmployee{id=1, name='Naresh', dob=Sun Jan 10 00:12:00 IST 1993}

Now if we do not initialize dob fields by deep copying then both dob and employee.dob will point to a single object and if we change anything in dob, employee.dob will also reflect that change which means employee object will become mutable.

But by deep copying dob field both employee.dob and dob will point to two different objects and we will not face this problem, as you can see output of below code

dob.setMonth(1);
System.out.println(dob); // Prints - Wed Feb 10 00:12:00 IST 1993

System.out.println(employee.getDob()); // Prints - Sun Jan 10 00:12:00 IST 1993
System.out.println(employee); // Prints - ImmutableEmployee{id=1, name='Naresh', dob=Sun Jan 10 00:12:00 IST 1993}

In our example, I have used the copy constructor this.dob = new Date(dob.getTime()); to copy our objects because there are some basic problems with Java cloning and we can not sure of either it is a deep copy or shallow copy without seeing cloning code of that class.

You can read more about cloning, cloning types and why copy constructors are better than on more detailed articles such as Java Cloning And Types Of Cloning (Shallow and Deep), Java Cloning - Copy Constructor Versus Cloning and Java Cloning - Even Copy Constructors Are Not Sufficient.

6. Why should not provide setters for your fields

Well, providing setters will allow us to modify the state of the object which we do not want.

7. Why return a deep copy of mutable fields instead of returning objects from the getters.

If we return all mutable fields directly, we will face the same scenario as discussed in point 5 and after executing below code both employee.dob and temp will point to the same object, now if we make any change in temp, employee.dob will also change which again means employee will not remain immutable.

So instead of returning mutable fields, we should return their deep copy and as we have done that, we can see in below code employee remains same and immutable at the end.

Date temp = employee.getDob();
temp.setMonth(2);
System.out.println(temp); // Prints - Wed Mar 10 00:12:00 IST 1993

System.out.println(employee.getDob()); // Prints - Sun Jan 10 00:12:00 IST 1993
System.out.println(employee); // Prints - ImmutableEmployee{id=1, name='Naresh', dob=Sun Jan 10 00:12:00 IST 1993}

In the end, I wanted to say that all immutable objects in Java are effective immutable not completely immutable because we can modify them using reflection.

Below is the complete source code to create an immutable class which you can also find on this Github Repository and please feel free to provide your valuable feedback.

// 1. Declare your class as final, So other classes can't extend it and break its immutability
final class ImmutableEmployee {

    // 2. Make all your fields private they can't be accessed outside your class
    // 3. Mark them as final so no one can modify them anywhere else apart from the constructor, if you do not have any specific requirement to not do so
    private final int id;
    private final String name;
    private final Date dob;

    // 4. Create an constructor with argument so you can assign instantiate your object with a proper state
    public ImmutableEmployee(int id, String name, Date dob) {
        this.id = id;
        this.name = name;
        // 5. Initialise all your fields by deeply copying them if they are not immutable in nature
        this.dob = new Date(dob.getTime());
    }

    // 6. Do not provide setters for your fields, or define them private if you have some requirement
    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    // 7. Instead of returning objects from the getters return deep copy them if your objects are not immutable
    public Date getDob() {
        return new Date(dob.getTime());
    }

    @Override
    public String toString() {
        return "ImmutableEmployee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", dob=" + dob +
                '}';
    }

}

public class ImmutableClassExample {

    public static void main(String[] args) throws ParseException {
        Date dob = new SimpleDateFormat("dd-mm-yyyy").parse("10-12-1993");
        ImmutableEmployee employee = new ImmutableEmployee(1, "Naresh", dob);

        System.out.println(employee); // Prints - ImmutableEmployee{id=1, name='Naresh', dob=Sun Jan 10 00:12:00 IST 1993}

        dob.setMonth(1);
        System.out.println(dob); // Prints - Wed Feb 10 00:12:00 IST 1993

        Date temp = employee.getDob();
        temp.setMonth(2);
        System.out.println(temp); // Prints - Wed Mar 10 00:12:00 IST 1993

        System.out.println(employee.getDob()); // Prints - Sun Jan 10 00:12:00 IST 1993
        System.out.println(employee); // Prints - ImmutableEmployee{id=1, name='Naresh', dob=Sun Jan 10 00:12:00 IST 1993}
    }
}


Previous Post Older Posts Home