Showing posts with label What in Java. Show all posts
Showing posts with label What in 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 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.

Java allows us to declare a variable whenever we need it, We can categorize all our variables into 3 categories which have different-different scopes
  1. Instance Variables - are defined inside a class and have object level scope.
  2. Class Variables - are defined inside a class with static keyword, these variables have a class level scope and are common to all objects of the same class
  3. Local Variables - are defined inside a method or in any conditional block, have the block-level scope and only accessible in the block where it defined.
what-is-variable-hiding-shadowing

What is Variable Shadowing

Variable shadowing happens when we define a variable in a closure scope with a variable name and we have already defined a variable in outer scope with the same name.

In other words, when a local variable has the same name as one of the instance variable, the local variable shadows the instance variable inside the method block.

In the following example, there is an instance variable named x and inside method printLocalVariable(), we are shadowing it by the local variable x.

class Parent {

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

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

    public void printLocalVariable() {
        // Shadowing instance variable `x` by a local variable with same name
        String x = "Local Variable";
        System.out.println(x);

        // If we still want to access instance variable, we do that by using `this.x`
        System.out.println(this.x);
    }
}

What is variable Hiding

Variable Hiding happens when we define a variable in child class with a variable name which we have already used to define a variable in the parent class. A child class can declare a variable with the same name as an inherited variable from its parent class, thus hiding the inherited variable.

In other words, when the child and parent class both have a variable with same name child class's variable hides parent class's variable.

In the below example, we are hiding the variable named x in the child class while it is already defined by its parent class.

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 printInstanceVariable() {
        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");
    }
}

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 variables.

In the case of method overriding, overridden 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 on Everything About Method Overloading Vs Method Overriding, Why We Should Follow Method Overriding Rules, How Does JVM Handle Method Overloading and Overriding Internally.

But in variable hiding child class hides the inherited variables instead of replacing, so when we try to access the variable from parent's reference by holding child's object, it will be accessed from the parent class.

When an instance variable in a subclass has the same name as an instance variable in a super class, then the instance variable is chosen from the reference type.

public static void main(String[] args) throws Exception {

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

    Child child = new Child();
    child.printInstanceVariable();// Output - "Child`s Instance Variable, Parent`s Instance Variable"
    System.out.println(child.x);// Output - "Child`s Instance Variable"

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

    // Accessing child's variable from parent's reference by type casting
    System.out.println(((Child) parent).x);// Output - "Child`s Instance Variable"
}

In above example when we call overridden method printInstanceVariable() on parent while holding Child's object in it we can see the output is Child`s Instance Variable, Parent`s Instance Variable because in child class method is printing Child class's x variable and super.x.

But when we call System.out.println(parent.variable); on same parent reference which is holding child's object, it prints Parents Instance Variable because new Child() object keeps parent's x as well as child's x and hides parent's x. So, in this case, x is chosen from the class that is the reference type.

But if we wanted to access child's variable even if we are using parent reference we can do that by using (Child) parent).variable.

When our variables are private or is in another package and has default access, such variables are not visible outside that class and child class cannot access them. So there no confusion and that is why we should always stick to General Guidelines to create POJOs and declare our variables with private access and also provide proper get/set methods to access them.

Do you want to know more about variable hiding?, In the article Why Instance Variable Of Super Class Is Not Overridden In Sub Class,  I have discussed why variables do not follow overriding, why variable hiding is not designed same as method overriding and why instance variable is chosen from reference type instead of the object? Please go ahead and read it.

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

In a previous article Everything About ClassNotFoundException Vs NoClassDefFoundError, I have explained ClassNotFoundException and NoClassDefFoundError in details and also discussed their differences and how to avoid them. If you have not read it, please go ahead and give it a look.

Similar to that here in this article, we will look into one more core concept of Java which is method overloading and overriding. As soon as we start learning Java we get introduced to them and their contracts which are pretty simple to understand but sometimes programmers get confused between them or they do not know either it is a correct overload/override because of the different rules.

Here we will discuss What is method overloading and overriding, What contract one must follow to correctly overload or override a method, What are the different rules of method overloading and overriding and what are the differences between them.

Method Overloading

Method overloading means providing two separate methods in a class with the same name but different arguments while method return type may or may not be different which allows us to reuse the same method name.

And this becomes very handy for the consumer of our class, he can pass different types of parameters to the same method (in his eyes but actually they are different) and get the response according to his input e.g. System.out.println() method accepts all types of objects primitive types and print them but in reality there several println present in the PrintStream class.

public class PrintStream {

    // other methods

    public void println() { /*code*/ }
    public void println(boolean x) { /*code*/ }
    public void println(char x) { /*code*/ }
    public void println(int x) { /*code*/ }
    public void println(long x) { /*code*/ }
    public void println(float x) { /*code*/ }
    public void println(double x) { /*code*/ }
    public void println(char x[]) { /*code*/ }
    public void println(String x) { /*code*/ }
    public void println(Object x) { /*code*/ }
    
    // other methods
}

While overloading has nothing to deal with polymorphism but Java programmers also refer method overloading as Compile Time Polymorphism because which method is going to get called will be decided at compile time only.

In the case of method overloading compiler decides which method is going to get called based on the reference on which it is getting called and the method name, return type, and argument list.

class Human {
    public String speak() { return "Hello"; }

    // Valid overload of speak
    public String speak(String language) {
        if (language.equals("Hindi")) return "Namaste";
        else return "Hello";
    }

    public long calculate(int a, long b) { return a + b; }

    // However nobody should do it but Valid overload of calculate
    // by just changing sequence of arguments
    public long calculate(long b, int a) { return a + b; }
}

Method Overloading Rules

There are some rules which we need to follow to overload a method and some of them are mandatory while some are optional.

Two methods will be treated as Overloaded if both follow below mandatory rule.
  • Both must have same method name
  • Both must have different argument lists
And if both methods follow above mandatory rules then they may or may not
  • Have different return types
  • Have different access modifiers
  • Throw different checked or unchecked exceptions
Usually, method overloading happens inside a single class but a method can also be treated as overloaded in the subclass of that class because subclass inherits one version of the method from the parent class and then can have another overloaded version in its class definition.

Method Overriding

Method Overriding means defining a method in the child class which is already defined in the parent class with same method signature i.e same name, arguments and return type (after Java 5 you can also use a covariant type as return type).

Whenever we extend a superclass in a child class, child class automatically gets all the methods defined in the super and we call them derived methods. But in some cases we do not want some derived methods to work in a manner which they are doing in the parent then we can override those methods in child class e.g. we always override equals, hashCode and toString from Object class, you can read more on Why can't we override clone() method from Object class.

In case of abstract methods either from a parent abstract class or interface we do not have any option we need to implement or in other words override all the abstract methods.

Method overriding is also known as Runtime Polymorphism and Dynamic Method Dispatch because which method is going to get called is decided at runtime by JVM.

abstract class Mammal {
    // Well might speak something
    public String speak() { return "ohlllalalalalalaoaoaoa"; }
}

class Cat extends Mammal {
    @Override
    public String speak() { return "Meow"; }
}

class Human extends Mammal {
    @Override
    public String speak() { return "Hello"; }
}

Using @Override annotation on the overridden methods is not necessary but using it will tell you if you are not obeying overriding rules. 

Mammal mammal = new Cat();
System.out.println(mammal.speak()); // Will print Meow

At the line mammal.speak() compiler says the speak() method of reference type Mammal is getting called, so for compiler this call is Mammal.speak().

But at the execution time JVM knows clearly that mammal reference is holding the reference of object of Cat, so for JVM this call is Cat.speak(). You can read more on How Does JVM Handle Method Overloading and Overriding Internally.

Method Overriding Rules

Similar to method overloading we also have some mandatory and some optional rules which we need to follow to override a method.

With respect to the method it overrides, the overriding method must follow below mandatory rule.
  • It must have same method name
  • Must have same arguments.
  • Must have the same return type, from Java 5 the return type can also be a subclass (Subclass is a covariant type to its parent).
  • Must not have a more restrictive access modifier (if parent --> protected then child --> private is not allowed).
  • Must not throw new or broader checked exceptions.
And if both overriding methods follow above mandatory rules then it
  • May have a less restrictive access modifier (if parent --> protected then child --> public is allowed).
  • May throw fewer or narrower checked exceptions or any unchecked exception.
Apart from above rules, there are also some facts
  • Only inherited methods can be overridden, Means methods can be overridden in child class only.
  • Constructors and private methods are not inherited so cannot be overridden.
  • Abstract methods must be overridden by the first concrete (non-abstract) subclass.
  • final methods cannot be overridden.
  • A subclass can use super.overridden_method() to call the superclass version of an overridden method.

Difference Between Method Overloading and Method Overriding


difference-between-method-overloading-and-method-overriding

You can find complete code on this Github Repository and please feel free to provide your valuable feedback.
We know Java is an Object Oriented Programming Language and almost everything is an object in Java and in order to create an object we need a class.

While executing our program whenever JVM find a class, First JVM will try to load that class into memory if it has not done it already.

For Example, If JVM is executing below the line of code, before creating the object of Employee class JVM will load this class into memory using a ClassLoader.

Employee emp = new Employee();

In above example, JVM will load the Employee class because it is present in the execution path and JVM want to create an object of this class.

But we can also ask JVM to just load a class through its string name using Class.forName() or ClassLoader.findSystemClass() or ClassLoader.loadClass() methods. For Example below the line of code will only load the Employee class into memory and do nothing else.

Class.forName("Employee");

Both ClassNotFoundException and NoClassDefFoundError occur when a particular class is not found at run time but under different scenarios. And here in this article, we going to study these different scenarios.

ClassNotFoundException

Is a checked exception that occurs when we tell JVM to load a class by its string name using Class.forName() or ClassLoader.findSystemClass() or ClassLoader.loadClass() methods and mentioned class is not found in the classpath.

Most of the time, this exception occurs when you try to run an application without updating the classpath with required JAR files. For Example, You may have seen this exception when doing the JDBC code to connect to your database i.e.MySQL but your classpath does not have JAR for it.

If we compile below example, the compiler will produce two class files Test.class and Person.class. And Now if we execute the program it will successfully print Hello. But if we delete Person.class file and again try to execute the program we will receive ClassNotFoundException.

public class Test {
    public static void main(String[] args) throws Exception {

        // ClassNotFoundException Example
        // Provide any class name to Class.forName() which does not exist
        // Or compile Test.java and then manually delete Person.class file so Person class will become unavailable
        // Run the program using java Test

        Class clazz = Class.forName("Person");
        Person person = (Person) clazz.newInstance();
        person.saySomething();
    }
}

class Person {
    void saySomething() {
        System.out.println("Hello");
    }
}

NoClassDefFoundError

Is a subtype of java.lang.Error and Error class indicates an abnormal behavior which really should not happen with an application but and application developers should not try to catch it, it is there for JVM use only.

NoClassDefFoundError occurs when JVM tries to load a particular class that is the part of your code execution (as part of a normal method call or as part of creating an instance using the new keyword) and that class is not present in your classpath but was present at compile time because in order to execute your program you need to compile it and if you are trying use a class which is not present compiler will raise compilation error.

Similar to above example if we try to compile below program, we will get two class files Test.class and Employee.class. A and on execution it will print Hello.

public class Test {
    public static void main(String[] args) throws Exception {

        // NoClassDefFoundError Example
        // Do javac on Test.java, 
        // Program will compile successfully because Empoyee class exits
        // Manually delete Employee.class file
        // Run the program using java Test
        Employee emp = new Employee();
        emp.saySomething();

    }
}

class Employee {
    void saySomething() {
        System.out.println("Hello");
    }
}

But if we delete Employee.class and try to execute the program we will get NoClassDefFoundError.

Exception in thread "main" java.lang.NoClassDefFoundError: Employee
 at Test.main(Test.java:9)
Caused by: java.lang.ClassNotFoundException: Employee
 at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
 ... 1 more

As you can see in above stack trace NoClassDefFoundError is caused by ClassNotFoundException, because JVM is not able to find the Employee class in the class path.

Conclusion

Difference-Between-ClassNotFoundException-and-NoClassDefFoundError

You can find complete code on this Github Repository and please feel free to provide your valuable feedback.
In my previous article Java Cloning and Types of Cloning (Shallow and Deep) in Details with Example, I have discussed Java Cloning in details and answered questions about how we can use cloning to copy objects in Java, what are two different types of cloning (Shallow & Deep) and how we can implement both of them, if you haven’t read it please go ahead.

In order to implement cloning, we need to configure our classes to follow the below steps
  • Implement Cloneable interface in our class or its superclass or interface,
  • Define clone() method which should handle CloneNotSupportedException (either throw or log),
  • And in most cases from our clone() method we call the clone() method of the superclass.
Java Cloning versus Copy Constructor

And super.clone() will call its super.clone() and the chain will continue until call will reach to clone() method of the Object class which will create a field by field mem copy of our object and return it back.

Like everything Cloning also comes with its advantages and disadvantages. However, Java cloning is more famous its design issues but still, it is the most common and popular cloning strategy present today.

Advantages of Object.clone()

Object.clone() have many design issues but it is still the popular and easiest way  of copying objects, Some advantages of using clone() are
  • Cloning requires very less line of code, just an abstract class with 4 or 5 line long clone() method but we will need to override it if we need deep cloning.
  • It is the easiest way of copying object especially if we are applying it to an already developed or an old project. We just need to define a parent class, implement Cloneable in it, provide the definition of clone() method and we are ready every child of our parent will get the cloning feature. 
  • We should use clone to copy arrays because that’s generally the fastest way to do it.
  • As of release 1.5, calling clone on an array returns an array whose compile-time
    type is the same as that of the array being cloned which clearly means calling clone on arrays do not require typecasting.

Disadvantages of Object.clone()

Below are some cons due to which many developers don't use Object.clone()
  • Using Object.clone() method requires us to add lots of syntax to our code like implement Cloneable interface, define clone() method and handle CloneNotSupportedException and finally call to Object.clone() and cast it our object.
  • The Cloneable interface lacks clone() method, actually, Cloneable is a marker interface and doesn’t have any method in it and still, we need to implement it just to tell JVM that we can perform clone() on our object.
  • Object.clone() is protected so we have to provide our own clone() and indirectly call Object.clone() from it.
  • We don’t have any control over object construction because Object.clone() doesn’t invoke any constructor.
  • If we are writing the clone method in a child class e.g. Person then all of its superclasses should define clone() method in them or inherit it from another parent class otherwise super.clone() chain will fail.
  • Object.clone() support only shallow copy so reference fields of our newly cloned object will still hold objects which fields of our original object were holding. In order to overcome this, we need to implement clone() in every class whose reference our class is holding and then call their clone them separately in our clone() method like in below example.
  • We can not manipulate final fields in Object.clone() because final fields can only be changed through constructors. In our case, if we want every Person objects to be unique by id we will get the duplicate object if we use Object.clone() because Object.clone() will not call the constructor and final final id field can’t be modified from Person.clone().
class City implements Cloneable {
    private final int id;
    private String name;
    public City clone() throws CloneNotSupportedException {
    return (City) super.clone();
    }
}

class Person implements Cloneable {
    public Person clone() throws CloneNotSupportedException {
        Person clonedObj = (Person) super.clone();
        clonedObj.name = new String(this.name);
        clonedObj.city = this.city.clone();
        return clonedObj;
    }
}

Because of the above design issues with Object.clone() developers always prefer other ways to copy objects like using
  • BeanUtils.cloneBean(object) creates a shallow clone similar to Object.clone().
  • SerializationUtils.clone(object) creates a deep clone. (i.e. the whole properties graph is cloned, not only the first level), but all classes must implement Serializable.
  • Java Deep Cloning Library offers deep cloning without the need to implement Serializable.
All these options require the use of some external library plus these libraries will also be using Serialization or Copy Constructors or Reflection internally to copy our object. So if you don’t want to go with the above options or want to write our own code to copy the object then you can use
  1. Serialization
  2. Copy Constructors

Serialization

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.

We can do it like it is done below or we can also use other APIs like JAXB which supports serialization.

// Method to deep clone an object using in memory serialization.
public Employee copy(Person original) 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(original);

    // 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 (Person) in.readObject();
}

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, you can read more on How To Deep Clone An Object Using Java In Memory Serialization.

Copy Constructors

This method copying object is most popular between developer community it overcomes every design issue of Object.clone() and provides better control over object construction

public Person(Person original) {
    this.id = original.id + 1;
    this.name = new String(original.name);
    this.city = new City(original.city);
}

Advantages of copy constructors over Object.clone()

Copy constructors are better than Object.clone() because they
  • Don’t force us to implement any interface or throw any exception but we can surely do it if it is required.
  • Don’t require any type of cast.
  • Don’t require us to depend on an unknown object creation mechanism.
  • Don’t require parent class to follow any contract or implement anything.
  • Allow us to modify final fields.
  • Allow us to have complete control over object creation, we can write our initialization logic in it.
By using Copy constructors strategy, we can also create conversion constructors which can allow us to convert one object to another object e.g. ArrayList(Collection<? extends E> c) constructor generates an ArrayList from any Collection object and copy all items from Collection object to newly created ArrayList object.


In my previous article 5 Different ways to create objects in Java with Example, I have discussed 5 different ways (new keyword, Class.newInstance() method, Constructor.newInstance() method, cloning and serialization-deserialization) a developer can use to create objects, if you haven't read it please go ahead.

Cloning is also a way of creating an object but in general, cloning is not just about creating a new object. Cloning means creating a new object from an already present object and copying all data of a given object to that new object.

In order to create a clone of an object we generally design our class in such a way that
  1. Our class should implement Cloneable interface otherwise, JVM will throw CloneNotSupportedException if we will call clone() on our object.
    Cloneable interface is a marker interface which JVM uses to analyse whether this object is allowed for cloning or not. According to JVM if yourObject instanceof Cloneable then create a copy of the object otherwise throw CloneNotSupportedException.
  2. Our class should have a clone method which should handle CloneNotSupportedException.
    It is not necessary to define our method by the name of clone, we can give it any name we want e.g. createCopy(). Object.clone() method is protected by its definition so practically child classes of Object outside the package of Object class (java.lang) can only access it through inheritance and within itself. So in order to access clone method on objects of our class we will need to define a clone method inside our class and then class Object.clone() from it. For details on protected access specifier, please read Why an outer Java class can’t be private or protected .
  3. And finally, we need to call the clone() method of the superclass, which will call it's super’s clone() and this chain will continue until it will reach to clone() method of the Object class. Object.clone() method is the actual worker who creates the clone of your object and another clone() methods just delegates the call to its parent’s clone().
All the things also become disadvantage of the cloning strategy to copy the object because all of above steps are necessary to make cloning work. But we can choose to not do all above things follow other strategies e.g. Copy Constructors, To know more read Java Cloning - Copy Constructor versus Cloning.
Java Cloning (Shallow Cloning, Deep Cloning)Example

To demonstrate cloning we will create two classes Person and City and override
  1. toString() to show the content of the person object,
  2. equals() and hashCode() method to compare the objects,
  3. clone() to clone the object
However, we are overriding the clone method but it not necessary we can create a clone method by any name but if we are naming it as clone then we will need to follow basic rules of method overriding e.g. method should have same name, arguments and return type (after Java 5 you can also use a covariant type as return type). To know more about method overriding read Everything About Method Overloading Vs Method Overriding.

class Person implements Cloneable {
    private String name; // Will holds address of the String object, instead of object itself
    private int income; // Will hold bit representation of int, which is assigned to it
    private City city; // Will holds address of the City object, instead of City object

    public String getName() {
        return name;
    }
    public void setName(String firstName) {
        this.name = firstName;
    }
    public int getIncome() {
        return income;
    }
    public void setIncome(int income) {
        this.income = income;
    }
    public City getCity() {
        return city;
    }
    public void setCity(City city) {
        this.city = city;
    }

    public Person(String firstName, int income, City city) {
        super();
        this.name = firstName;
        this.income = income;
        this.city = city;
    }

    // But we can also create using any other name
    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }

    // To print the person object
    @Override
    public String toString() {
        return "Person [name=" + name + ", income=" + income + ", city=" + city + "]";
    }

    // hasCode(), and equals() to compare person objects
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((city == null) ? 0 : city.hashCode());
        result = prime * result + income;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (city == null) {
            if (other.city != null)
                return false;
        } else if (!city.equals(other.city))
            return false;
        if (income != other.income)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

Person class has a reference to City class which looks like below

class City implements Cloneable {
    private String name;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public City(String name) {
        super();
        this.name = name;
    }

    @Override
    public City clone() throws CloneNotSupportedException {
        return (City) super.clone();
    }

    @Override
    public String toString() {
        return "City [name=" + name + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        City other = (City) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

And let's test it

public class CloningExample {

    public static void main(String[] args) throws CloneNotSupportedException {

        City city = new City("Dehradun");
        Person person1 = new Person("Naresh", 10000, city);
        System.out.println(person1);

        Person person2 = person1.clone();
        System.out.println(person2);

        if (person1 == person2) { // Evaluate false, because person1 and person2 holds different objects
            System.out.println("Both person1 and person2 holds same object");
        }

        if (person1.equals(person2)) { // Evaluate true, person1 and person2 are equal and have same content
            System.out.println("But both person1 and person2 are equal and have same content");
        }

        if (person1.getCity() == person2.getCity()) {
            System.out.println("Both person1 and person2 have same city object");
        }
    }
}

person1.clone() calls super.clone() which means Object.clone() method.
Object.clone() method copy content of the object to other object bit-by-bit, means the values of all instance variables from one object will get copied to instance variables of other object.


So (person1 == person2) will evaluate false because person1 and person2 are the copy of each other but both are different objects and holds different heap memory. While person1.equals(person2) evaluate true because both have the same content.

But as we know reference variables holds address of the object instead of object itself, which can also be referred from other reference variables and if we change one other will reflect that change.

So while cloning process Object.clone() will copy address which person1.city is holding to person2.city, So now city, person1.city, and person2.city all are holding the same city object. That’s why (person1.getCity() == person2.getCity()) evaluate true. This behaviour of cloning is known as Shallow Cloning.

Types of Cloning

This behaviour of Object.clone() method classifies cloning into two sections

1. Shallow Cloning

Default cloning strategy provided by Object.clone() which we have seen. The clone() method of object class creates a new instance and copy all fields of the Cloneable object to that new instance (either it is primitive or reference). So in the case of reference types only reference bits get copied to the new instance, therefore, the reference variable of both objects will point to the same object. The example we have seen above is an example of Shallow Cloning.

2. Deep Cloning

As the name suggests deep cloning means cloning everything from one object to another object. To achieve this we will need to trick our clone() method provide our own cloning strategy. We can do it by implementing Cloneable interface and override clone() method in every reference type we have in our object hierarchy and then call super.clone() and these clone() methods in our object’s clone method.

So we can change the clone method of Person class in below way

public Person clone() throws CloneNotSupportedException {
    Person clonedObj = (Person) super.clone();
    clonedObj.city = this.city.clone();
    return clonedObj;
}

Now (person1.getCity() == person2.getCity()) will evaluate false because in clone() method of Person class we are clonning city object and assigning it to the new clonned person object.

In below example we have deep copied city object by implementing clone() in City class and calling that clone() method of person class, That's why person1.getCity() == person2.getCity() evaluate false because both are separate objects. But we have not done same with Country class and person1.getCountry() == person2.getCountry() evaluate true.

public class CloningExample {

    public static void main(String[] args) throws CloneNotSupportedException {

        City city = new City("Dehradun");
        Country country = new Country("India");
        Person person1 = new Person("Naresh", 10000, city, country);
        System.out.println(person1);

        Person person2 = person1.clone();
        System.out.println(person2);

        // Evaluate false, because person1 and person2 holds different objects
        if (person1 == person2) {
            System.out.println("Both person1 and person2 holds same object");
        }

        // Evaluate true, person1 and person2 are equal and have same content
        if (person1.equals(person2)) {
            System.out.println("But both person1 and person2 are equal and have same content");
        }

        // Evaluate false
        if (person1.getCity() == person2.getCity()) {
            System.out.println("Both person1 and person2 have same city object");
        }

        // Evaluate true, because we have not implemented clone in Country class
        if (person1.getCountry() == person2.getCountry()) {
            System.out.println("Both person1 and person2 have same country object");
        }

        // Now lets change city and country object and print person1 and person2
        city.setName("Pune");
        country.setName("IN");

        // person1 will print new Pune city
        System.out.println(person1);
        // while person2 will still print Dehradun city because person2.city holds a separate city object
        System.out.println(person2);
    }
}

class Person implements Cloneable {
    private String name; // Will holds address of the String object which lives
                         // in SCP, instead of String object itself
    private int income; // Will hold bit representation of int, which is assigned to it
    private City city; // Will holds address of the City object which lives in
                        // heap, instead of City object
    private Country country;

    public String getName() {
        return name;
    }

    public void setName(String firstName) {
        this.name = firstName;
    }

    public int getIncome() {
        return income;
    }

    public void setIncome(int income) {
        this.income = income;
    }

    public City getCity() {
        return city;
    }

    public void setCity(City city) {
        this.city = city;
    }

    public Country getCountry() {
        return country;
    }

    public void setCountry(Country country) {
        this.country = country;
    }

    public Person(String name, int income, City city, Country country) {
        super();
        this.name = name;
        this.income = income;
        this.city = city;
        this.country = country;
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        Person clonedObj = (Person) super.clone();
        clonedObj.city = this.city.clone();
        return clonedObj;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", income=" + income + ", city=" + city + ", country=" + country + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((city == null) ? 0 : city.hashCode());
        result = prime * result + ((country == null) ? 0 : country.hashCode());
        result = prime * result + income;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (city == null) {
            if (other.city != null)
                return false;
        } else if (!city.equals(other.city))
            return false;
        if (country == null) {
            if (other.country != null)
                return false;
        } else if (!country.equals(other.country))
            return false;
        if (income != other.income)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

class City implements Cloneable {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public City(String name) {
        super();
        this.name = name;
    }

    public City clone() throws CloneNotSupportedException {
        return (City) super.clone();
    }

    @Override
    public String toString() {
        return "City [name=" + name + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        City other = (City) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

class Country {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Country(String name) {
        super();
        this.name = name;
    }

    @Override
    public String toString() {
        return "Country [name=" + name + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Country other = (Country) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

Java cloning is not considered a good way to copy an object and lots of other ways are there to do the same. You can read Java Cloning - Copy Constructor versus Cloning to get more knowledge on why Java cloning is not a preferred way of cloning and what are other ways to overcome this.
Previous Post Older Posts Home