How to Customize Serialization in Java Using the Externalizable Interface

In a previous article, Everything You Need to Know About Java Serialization Explained, I explained how we can serialize/deserialize one object using the Serializable interface and explain how we can customize the serialization process using writeObject and readObject methods.

Disadvantages of Java Serialization Process

But these customizations are not sufficient because the 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 we 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 tries 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 that we do not want to serialize for some reason (these fields will always be assigned with default values). With the default serialization process, we will have to make all these fields transient but it still will not be efficient because there will a lot of checks to see if the fields are transient or not.

So as we can see, there are lots of downsides to using the 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 the Externalization and Externalizable Interface?

As we saw above, 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 the JVM and it is up to us to provide the complete serialization and deserialization logic.

So, it is very necessary to code and test these methods 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 our first preference is given to these methods over the default serialization mechanism provided by the 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 is called. Serializable objects are restored by reading them from an ObjectInputStream.

  1. When an Externizable object is reconstructed, an 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 three-article long Java Cloning Series, which includes articles like Java Cloning and Types of Cloning (Shallow and Deep) in Details With ExampleJava Cloning — Copy Constructor Versus Cloning, and 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. Please feel free to provide your valuable feedback in the comments below.

Originally published by  Naresh Joshi  at dzone.com

=====================================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Everything You Need to Know About Java Serialization Explained

How to create a simple web application with Java 8, Spring Boot and Angular

Java vs Golang: Choosing a language for Freshdesk Microservices


#java #web-development

What is GEEK

Buddha Community

How to Customize Serialization in Java Using the Externalizable Interface
Tyrique  Littel

Tyrique Littel

1600135200

How to Install OpenJDK 11 on CentOS 8

What is OpenJDK?

OpenJDk or Open Java Development Kit is a free, open-source framework of the Java Platform, Standard Edition (or Java SE). It contains the virtual machine, the Java Class Library, and the Java compiler. The difference between the Oracle OpenJDK and Oracle JDK is that OpenJDK is a source code reference point for the open-source model. Simultaneously, the Oracle JDK is a continuation or advanced model of the OpenJDK, which is not open source and requires a license to use.

In this article, we will be installing OpenJDK on Centos 8.

#tutorials #alternatives #centos #centos 8 #configuration #dnf #frameworks #java #java development kit #java ee #java environment variables #java framework #java jdk #java jre #java platform #java sdk #java se #jdk #jre #open java development kit #open source #openjdk #openjdk 11 #openjdk 8 #openjdk runtime environment

Samanta  Moore

Samanta Moore

1620458875

Going Beyond Java 8: Local Variable Type Inference (var) - DZone Java

According to some surveys, such as JetBrains’s great survey, Java 8 is currently the most used version of Java, despite being a 2014 release.

What you are reading is one in a series of articles titled ‘Going beyond Java 8,’ inspired by the contents of my book, Java for Aliens. These articles will guide you step-by-step through the most important features introduced to the language, starting from version 9. The aim is to make you aware of how important it is to move forward from Java 8, explaining the enormous advantages that the latest versions of the language offer.

In this article, we will talk about the most important new feature introduced with Java 10. Officially called local variable type inference, this feature is better known as the **introduction of the word **var. Despite the complicated name, it is actually quite a simple feature to use. However, some observations need to be made before we can see the impact that the introduction of the word var has on other pre-existing characteristics.

#java #java 11 #java 10 #java 12 #var #java 14 #java 13 #java 15 #verbosity

Samanta  Moore

Samanta Moore

1624974840

Functional Interfaces in Java 8

Introduction

Functional interface is an interface that consists of one abstract method. These interface can show only one functionality. Beyond Java 8, _lambda expressions _can be used to represent the instance of a functional interface. Functional Interfaces can contain any number of default methods. ConsumerPredicate, Function, Unary Operator, Binary Operator are some of the examples of predefined functional interfaces.

Points to remember which are allowed and which are not allowed in a functional interface:

  • In any functional interface, single abstract method is allowed.
  • In a functional interface, more than one abstract method cannot exist.
  • We can have more than one abstract method if we remove @FunctionalInterface  annotation, but that interface cannot be functional interface.

From functional interface if we remove @FunctionalInterface annotation, it must be valid. We use an annotation so that only single abstract method can be present. In functional interface we can provide implementations as the default methods inside interface.

#functional programming #java #java #java 8 #functional interfaces #functional interfaces in java 8

Samanta  Moore

Samanta Moore

1623861240

What is Assertion in Java? How to use Assertion in Java

Table of Contents

What is Assertion in Java?

The assertion in Java is used to ensure the correctness of any program’s assumptions as the assertion is assumed to be true when it is executed. The Java Virtual Machine throws an error named AssertionError if the assertion is false. The assertion in Java has found its application mainly for testing purposes. Boolean expressions are used along with assertion statements. An assertion is a statement, and the ‘asset’ keyword is used to carry out an assertion in Java.

#full stack development #assertion in java #assertion in java #assertion in java #what is assertion in java #how to use assertion in java

Samanta  Moore

Samanta Moore

1620462686

Spring Boot and Java 16 Records

In this article, we will discuss Java 16’s newest feature, Records. Then we will apply this knowledge and use it in conjunction with a Spring Boot application.

On March 16th, 2021, Java 16 was GA. With this new release, tons of new exciting features have been added. Check out the release notes to know more about these changes in detail. This article’s focus will be on Java Records, which got delivered with JEP 395. Records were first introduced in JDK 14 as a preview feature proposed by JEP 359, and with JDK 15, they remained in preview with JEP 384. However, with JDK 16, Records are no longer in preview.

I have picked Records because they are definitely the most favored feature added in Java 16, according to this Twitter poll by Java Champion Mala Gupta.

I also conducted a similar survey, but it was focused on features from Java 8 onwards. The results were not unexpected, as Java 8 is still widely used. Very unfortunate, though, as tons of new features and improvements are added to newer Java versions. But in terms of features, Java 8 was definitely a game-changer from a developer perspective.

So let’s discuss what the fuss is about Java Records.

#java #springboot #java programming #records #java tutorials #java programmer #java records #java 16