Top 7 Interview Questions for Java Developers

This article talks about top 7 of the most frequently asked questions in the job interview for the Java developers. If we go through these questions and its respective answers, it would serve as a good preparation for facing the actual interview questions.

Java Interview Questions

1. Describe 4 fundamental principles of object-oriented programming, and give code examples to demonstrate those principles in Java.

 

Object-oriented programming is a program paradigm based on the concept of objects. An object may contain data, in the form of fields, and behavior, in the form of methods.

Object-oriented programming features 4 fundamental principles, including inheritance, polymorphism, abstraction and encapsulation.

  1. Inheritance

Inheritance is a mechanism where one type acquires, or inherits, data and behavior from another. This mechanism facilitates code reuse through type hierarchies.

The type that inherits from the other is called subtype (or child), and the one that is inherited from is called supertype (or parent). When inheritance is related to classes, they are called subclass and superclass; when it is about interfaces, they are called subinterface and superinterface.

Inheritance allows members of a supertype to be reused in its subtypes, while still lets subtypes declare their own fields and methods. Note that a supertype can be inherited by any number of subtypes, but a subtype can only directly inherit from a supertype.

In the Java programming language, inheritance can be achieved using the extends keyword. The following is an example of class inheritance:

public class Super {
    protected String name = "Whizlabs";
    protected void print() {
        System.out.println(name);

    }

}

public class Sub extends Super { }

Since the Sub class extends the Super class, it inherits the name field and print method from the superclass and can use these members without defining them.

Let’s run a code fragment:

Sub object = new Sub();
object.print();

The above fragment prints “Whizlabs” when executed, although the Sub class does not define any member of its own. The print method is, in fact, inherited from the Super class.

  1. Polymorphism

Polymorphism is a principle in which an object reference can take many different forms. Polymorphic features of the Java language are often demonstrated via the use of one reference variable referencing objects of various types. The reference type is the same as, or a parent of, the referenced object types. As a result, a method can behave differently depending on the actual objects it is invoked on.

The following class declarations illustrate this principle:

public class Super {
    protected void print() { }
}

public class Sub1 extends Super {
    public void print() {
        System.out.println("Printed from Sub1");
    }
}

public class Sub2 extends Super {
    public void print() {
        System.out.println("Printed from Sub2");
    }
}

Let’s execute the following code fragment:

Super object = null;
object = new Sub1();
object.print();
object = new Sub2();
object.print();

The above fragment prints out “Printed from Sub1” and “Printed from Sub2” in sequence, even though the print method is invoked twice on the same reference variable, object. The reason is that variable object references two different objects when these invocations are made. This discrepancy in method behavior is called polymorphism.

  1. Abstraction

Abstraction is a process of hiding the implementation details from users and exposing only essential features of the underlying objects. Generally speaking, abstraction allows users to know what an object does without the need to understand how it does.

In the Java programming language, abstraction is typically achieved through the use of interfaces and abstract classes. The following examples illustrate the abstraction principle. Let’s get started with the declaration of an interface and abstract class:

public interface MyInterface {
    void complicatedLogic();
}

public abstract class MyAbsClass {
    abstract void complicatedLogic();
}

A user can use the complicatedLogic method of MyInterface or MyAbsClass without knowing its implementation:

public void callInterfaceMethod(MyInterface arg) {
    arg.complicatedLogic ();
}

public void callAbsClassMethod(MyAbsClass arg) {
    arg.complicatedLogic ();
}

In the above examples, the MyInterface interface and MyAbsClass class abstract away the implementation details from the user, allowing the implementation provider to change the code if necessary.

  1. Encapsulation

Encapsulation is a mechanism of including within an object everything it needs, and doing this in a way that other objects cannot access its internal structure. In other words, encapsulation refers to wrapping data (variables) and code acting on the data (methods) together as a single unit. When applying encapsulation, variables of a class are hidden from others, and can only be accessed and operated on through methods of their own class.

In the Java programming language, encapsulation is often achieved by declaring variables of a class with the private access modifier and providing public methods to work on these variables, like the following example:

public class Data {
    private int value;
    public int getValue() {
        return this.value;
    }
    public void setValue(int value) {
        this.value = value;
    }
}

2. How does the Java virtual machine manage the lifecycle of an object in the memory?

  1. Background information

The lifecycle of an object starts when it is created with the new operator or a similar mechanism. However, it cannot be explicitly destroyed through an executable code. The only way to remove an object in Java is to use garbage collection.

When a new object is generated, it is stored in the heap memory, and kept there as long as this object is referenced from somewhere in the program. It can be referenced from a static variable in the metaspace, an instance variable in the heap memory itself, a local variable in the stack memory, etc.

The most interesting thing about the lifecycle of an object is when it is dereferenced, or no longer referenced from any section of the program. At this point, the object is eligible for automatic garbage collection and ready to be discarded.

Automatic garbage collection is the process of looking into the heap memory to mark live objects and remove the leftover. When an unreferenced object is removed, the memory space it occupied can be reclaimed to be reused. This process helps free developers of a tedious and error-prone task: allocating and deallocating memory.

  1. Generational garbage collection process

In general, the collection process works through two steps:

  • First, object trees are navigated from the garbage collection roots; these roots include local variables, static variables, active Java threads and Java native interface references. All objects in these trees are marked referenced.
  • Second, unreferenced objects, which have not been marked in the previous step, are removed and the free space is returned to the JVM. If a garbage collection process is major (major and minor garbage collections will be described later), referenced objects may be compacted, allowing new objects to occupy contiguous memory space.

Marking and removing objects is a time-consuming process, and always applying it to all objects is inefficient. Empirical analysis has shown that most objects have a very short life. To take advantage of this pattern and insure all unused objects are removed in an effective way, the idea of generational garbage collection has come up.

The following image illustrates memory allocation in a heap with generational garbage collection (the ratios between different areas in the image illustrate default values in a server JVM – Eden over S0 or S1 = 8:1, Young Generation over Old Generation = 1:2):

The young generation is the place where all new objects are allocated. If an object survives minor garbage collections, it will be moved to the old generation, where the object may be destroyed during a major collection, or live until the program terminates.

The young generation is comprised of three spaces – eden and two survivor spaces, S0 and S1:

  • The eden space is where all objects are first allocated. When this space fills up, a minor collection is triggered. Referenced objects are moved to the first survivor space (S0), while the unreferenced are deleted. At this point, the eden space is cleared and ready to keep new objects. Both eden and the second survivor space (S1) are empty now.
  • When the next minor collection cycle is set off, the same things happen again. Unused objects are discarded, and live objects are moved to a survivor space. This time, however, the survivors are moved to S1 instead of S0. Objects in S0 that survived the last collection may be deleted (due to being unreferenced), copied to S1, or promoted to the old generation. Now, both eden and S0 are clear.
  • The above steps are repeated over and over. An object will eventually be moved to the old generation if it survives all minor collections and a certain criterion is met, such as its age reaches a threshold, the number of times it is copied increases to a limit, or it is too large to be copied to the other survivor space.

The old generation is where long-lived objects that have survived minor collections are promoted to. This space is often garbage-collected when it fills up. A collection in the old generation may include object compaction, and is called major garbage collection.

All garbage collections are “Stop the World” events, meaning that all application threads are stopped until the operation finishes. A major collection is typically takes much longer than a minor one as it must go through all referenced objects. This will affect the responsiveness of the program. As a result, the type of the garbage collector in use may have an impact on JVM performance.

  1. Performance goals

Different applications have different requirements on garbage collections. Typically, a garbage collector focus on either of two main goals: throughput and responsiveness:

  • Throughput is the amount of work implemented by an application in a specific duration. Specifically, it is the percentage of total time not spent in garbage collection over a long period of time. This goal is important in a web server, for example, as the number of serviced requests is much more important than pause time, which may be hidden in the disguise of network latencies.
  • Responsiveness (pause time) refers to how quickly an application responds to a request. This is measured by the time when the application is unresponsive due to a garbage collection in progress. For instance, a game application should consider this goal one of the top priorities since even short pauses may negatively affect the user experience.

Some other goals may be taken into consideration, such as footprint or promptness. Footprint is the working set of a process, measured in pages and cache lines. This goal is critical in systems with limited physical memory. Promptness is the duration between an object becoming unused and the memory being reclaimed. This performance measure is important for distributed systems.

Generally, it is hard to achieve multiple performance goals at the same time. For instance, a large young generation may maximize throughput, but has a negative effect on responsiveness, footprint and promptness.

The Java HotSpot VM includes three different types of collectors to attain various performance goals:

  • A serial collector uses a single thread to perform all collection operations. This collector is efficient in systems with a single processor as no communication overhead between threads occurs.
  • A parallel collector performs minor collections in parallel. This can considerably reduce garbage collection overhead.
  • A mostly concurrent collector performs most of its work concurrently to minimize collection pauses and increase the responsive level.

3. What are annotations and how to define and use them?

Annotations are a form of metadata that describes the annotated code. Annotations themselves are not part of a program and declared to provide information for tools or other programs to work on. Here are several applications of annotations:

  • To be used by the compiler to detect errors or suppress warnings at compile time.
  • To be used by a container to set up the operational environment at deployment time.
  • To instruct the JVM to implement additional jobs at runtime.

An annotation is specified using the at sign character (@), which signifies that what follows in an annotation, such as @MyAnnotation. The following sections describe annotations in detail.

  1. Declaring annotation types

The definition of an annotation type is similar to that of an interface where the interface keyword is preceded by the @ character. An annotation definition may contain elements, whose declaration is akin to abstract methods and may consist of default values.

Here is an example of annotation type definitions:

@interface MyAnnotation {
    String url() default "whizlabs.com";
    String[] exams();
    int year();
}

The above annotation type defines three elements: url, exams and year. The values of these elements are of types String, String[] and int, respectively. If the value of url is not specified, it defaults to “whizlabs.com”.

The Java SE API declares several built-in useful annotations. One of the most commonly used is @Override. This annotation is useful during development as it helps to make sure that the annotated method actually overrides a superclass method or implements an interface one.

In addition to annotations applied to Java elements of other kinds, e.g. a field, method or type, there are a few annotations that only work on other annotations. These are called meta-annotations, which are important when defining a custom annotation type. Meta-annotations are described below:

  • @Retention annotation: Specifies how the marked annotation is stored, with the following allowable values:
  • SOURCE: The annotated annotation is used only for compile-time reports and ignored by the compiler when compiling the source code. This does not mean that the compiler completely skip the annotation. Instead, it may report errors based on the annotated annotation, but still goes ahead despite such an error.
  • CLASS: The annotated annotation is preserved by the compiler at compile time. It is compiled into the class file, but ignored by the Java virtual machine at runtime. This is the default behavior if no value is set.
  • RUNTIME: The annotated annotation is recorded in the class file and utilized by the Java virtual machine at runtime.
  • @Target annotation: Indicates the contexts in which the annotated annotation is applicable. The following are values allowed to be specified in this annotation:
  • ANNOTATION_TYPE: The annotated annotation can be applied to an annotation type.
  • CONSTRUCTOR: The annotated annotation can be applied to a constructor.
  • FIELD: The annotated annotation can be applied to a field or property.
  • LOCAL_VARIABLE: The annotated annotation can be applied to a local variable.
  • METHOD: The annotated annotation can be applied to a method.
  • PACKAGE: The annotated annotation can be applied to a package declaration.
  • PARAMETER: The annotated annotation can be applied to the parameters of a method.
  • TYPE: The annotated annotation can be applied to a class, interface (including annotation type) or enum.

In the absence of the @Target meta-annotation, an annotation may be used on any declarations except for the declaration of type parameters.

  • @Documented annotation: Indicates that an annotation is to be documented by javadoc or similar tools. This annotation is important to clients if the annotated annotation influences the use of the element it is declared on. By default, annotation types are not included in the generated Java API.
  • @Inherited annotation: Indicates that the annotated annotation can be inherited in a class hierarchy. If a parent class is declared with an @Inherited annotation, subclasses of this superclass can use the annotation without re-declaring it. The annotated annotation applies to type declarations only. By default, annotation types are not inheritable.
  • @Repeatable annotation: Indicates that the annotated annotation can be applied more than once to the same declaration or type use. The value of @Repeatableindicates the containing annotation type for the repeatable annotation type.
  1. Using annotations

Annotations are often applied to declaration of various Java elements, such as:

@MyAnnotation(exams = { "OCAJP", "OCPJP }, year = 2017)
public class Whizlabs {
    // class body
}

Or:

@Override
public void myMethod() { ... }

From Java SE 8, annotations can also be applied to the use of type. In other words, annotations can be used anywhere a type is used. Several examples are shown below:

  • Class instance creation expression: new @Interned MyObject();
  • Type cast: myString = (@NonNull String) string;
  • Thrown exception declaration: void myMethod() throws @Severe MyException { … }

Type annotations were created to support stronger type checking. The Java SE platform does not provide a type checking framework, but many third-party ones are available as pluggable modules that can be used with the Java compiler.

4. What is generics? Describe and illustrate generic types and generic methods.

  1. What is generics?

Generally, generics are a facility that allows types to be used as parameters when defining classes, interfaces and methods. Type parameters provide a way to re-use the same declaration with different type inputs. By using generics, you can implement type-safe algorithms that work on collections of different types.

The following are a couple of use cases demonstrating the application of generic code:

  • Stronger type checks at compile time: The compiler applies strong type checking when compiling generic code, raising an error if there is a violation. This helps to find many bugs at compile time, which otherwise only emerge at runtime. Fixing a bug at runtime is more expensive than doing so at compile time.
  • Elimination of casting: Prior to the introduction of generics, many operations need to be cast to the target type, such as:
Map map = new HashMap();
map.put("name", "Whizlabs");
String name = (String) map.get("name");

In this case, using generics can eliminate casting:

Map<String, String> map = new HashMap<String, String>();
map.put("name", "Whizlabs");
String name = map.get("name");
  1. Generic types

A generic type is a class or interface that is parameterized over types. A generic type is declared as follows:

class name<T1, T2, ..., Tn> { ... }
interface name<T1, T2, ..., Tn> { ... }

Type parameters T1, T2, …, Tn are enclosed within a pair of angle brackets following the name of the type being declared (a class or interface). These type parameters can be used anywhere in the class or interface.

The following is an example of the definition of a generic type:

public class Whizlabs<T> {
    private T t;
    public T get() {
        return t;
    }

    public void set(T t) {
        this.t = t;
    }
}

The above generic type can be invoked with any type that are not primitive. An invocation of a generic type, which is also called a parameterized type, replaces type parameters with specific type arguments. A parameterized type can then be used in the same way as a non-generic type:

Whizlabs<String> whizlabs = new Whizlabs<String>();

The type arguments used to invoke the constructor of a generic class in the above statement can be replaced by an empty set of type arguments (<>), also known as the diamond, if the compiler can determine the type arguments from the context:

Whizlabs<String> whizlabs = new Whizlabs<>();
  1. Generic methods

A generic method is a method with its own type parameters. Declaring type parameters for a generic method is similar to doing for a generic type. These type parameters are scoped to the method where it is declared. This is true for constructors as well.

The syntax of a generic method declaration is shown below:

modifiers <T1, T2, …, Tn> return-type method-name(parameters) { … }

Type parameters T1, T2, …, Tn are enclosed within a pair of angle brackets preceding the method’s return type. These parameters must follow all modifiers.

Here is an example:

public static <T> boolean compareEquality(T t1, T t2) {
    return t1.equals(t2);
}

The compareEquality method can be invoked as follows (assume it is defined in the Whizlabs class):

boolean check = Whizlabs.<Integer> compareEquality(1, 2);

If the compiler can infer the type argument from the context, it can be left out as follows:

boolean check = Whizlabs.compareEquality(1, 2);

5. Describe Java lambda expressions and give examples to demonstrate their usage.

A Java lambda expression is a function that is defined outside of any class. It was introduced to facilitate functional programming in the Java platform. Lambda expressions enable functionality to be treated as method argument, or code as data.

  1. Functional interfaces

Functional interfaces are interfaces that define exactly one abstract method. It may contain any number of non-abstract methods. The following is an example of functional interface:

interface MyInterface {
    void myMethod();
}

The following declaration is also a functional interface despite two additional methods:

interface MyInterface {
    void myMethod();
    default void defaultMethod() { ... }
    static void staticMethod() { ... }
}

Assume there is a method that has a parameter of type MyInterface:

public void handleMyInterface(MyInterface arg) {
    arg.myMethod();
}

Prior to Java SE 8, the most concise way to pass around an instance of a functional interface, like any other interface, is to use an anonymous class, like this:

handleMyInterface(new MyInterface() {
    public void myMethod() {
        System.out.println("Whizlabs");
    }
});

With the introduction of functional interfaces in Java SE 8, the above invocation can be even more succinct:

handleMyInterface(() -> System.out.println("Whizlabs"));

The syntax of lambda expressions is given in the following section.

  1. Lambda expression syntax

A lambda expression can only be used in places where a functional interface is expected. Its structure must correspond to the signature and return type of the unique abstract method of that functional interface.

A lambda expression consists of three elements:

  • A comma-separated list of formal parameters enclosed in parentheses
  • The arrow token (->)
  • A body, which consists of a single expression or a statement block

Let’s see an example now. Given a functional interface:

public interface NumberComparator {
    boolean compare(int i, int j);
}

The following lambda expression can be used anywhere an instance of the NumberComparator interface is required:

(int i, int j) -> { return i == j; }

The above lambda expression takes in two arguments of type int, and returns a boolean value. These types are the same as those of the abstract method of the corresponding interface, NumberComparator.

Since the body of the lambda expression is comprised of only one statement, and the parameter types can be inferred from the NumberComparator.compare method, the above lambda expression can be reduced to:

(i, j) -> i == j

6. Describe commonly used methods of three interfaces of the Java Collections API: List, Set and Map.

The Java Collections API provide many classes and interfaces that are useful to process a collection of Java objects. List, Set and Map are among the most commonly used interfaces. List and Set are subtypes of the Collection interface, while Map is not.

  1. The Collection interface

The Collection interface is used to represent a collection of objects. Its methods are inherited by subinterfaces, including List and Set.

The Collection interface contains methods that perform basic operations, e.g.:

  • size(): Returns the number of elements in this collection.
  • isEmpty(): Returns whether this collection contains no elements.
  • contains(Object): Returns whether this collection contains the specified element.
  • add(E): Insures that this collection contains the specified element.
  • remove(Object): Removes a single instance of the specified element from this collection if it is present.
  • iterator(): Returns an iterator over the elements in this collection.

This interface also defines methods that operate on entire collections, such as:

  • containsAll(Collection<?>): Returns true if this collection contains all of the elements in the specified collection.
  • addAll(Collection<? extends E>): Adds all of the elements in the specified collection to this collection.
  • retainAll(Collection<?>): Retains only the elements in this collection that are contained in the specified collection.
  • removeAll(Collection<?>): Removes all of this collection’s elements that are also contained in the specified collection.
  • clear(): Removes all of the elements from this collection.

Moreover, the Collection interface comprises methods converting a collection to an array or stream as well:

  • toArray(), toArray(T[]): Returns an array containing all of the elements in this collection.
  • stream(), parallelStream(): Returns a sequential/parallel Stream with this collection as its source.
  1. The Set interface

Set is a Collection that allows no duplicate elements, and no more than one null value. Specifically, a Set contain no pair of elements e1 and e2 such that e1.equals(e2). This interface contains only methods that are inherited from Collection and adds constraints that forbid duplication.

  1. The List interface

List is an ordered Collection allowing duplicate elements. This interface has methods for control over where to insert and access elements of the list. It also contains methods for searching for elements of a List.

List is a subinterface of Collection, thus inherits all methods defined in the Collection interface. In addition, the List interface supports other operations:

  • Positional access: Facilitates CRUD operations based on the element’s position in the list, for instance:
  • get(int): Returns the element at the specified position in this list.
  • set(int, E): Replaces the element at the specified position in this list with the specified element.
  • add(E): Appends the specified element to the end of this list.
  • add(int, E): Inserts the specified element at the specified position in this list.
  • remove(Object): Removes the first occurrence of the specified element from this list, if it is present
  • Iteration: Goes through the list, allowing actions to be taken on each element. Iteration can be obtained using overloaded listIterator The Iterator returned from these methods is a ListIterator that takes advantage of the list’s sequential characteristic.
  • listIterator(): Returns a list iterator over the elements in this list, starting from the beginning of the list.
  • listIterator(int index): Returns a list iterator over the elements in this list, starting at the specified position in the list.
  • Search: Looks for a specified object in the list and returns its numerical position. This kind of operation is provided by methods indexOf(Object) and lastIndexOf(Object). These methods return the index of the first/last occurrence of the specified element in this list, or -1 if this list does not contain the element.
  • The List interface also defines a method for range-view operations: subList(int, int). This method returns a view of the portion of this list between indexes specified by the first argument, inclusive, and the second, exclusive.
  1. The Map interface

Map is an object that maps keys to values. No duplicate keys are allowed in a map. Each key is associated with at most one value. This interface defines methods for various operations introduced below.

The Map interface consists of methods for basic operations, such as:

  • size(): Returns the number of key-value mappings in this map.
  • isEmpty(): Returns true if this map contains no key-value mappings.
  • put(K, V): Associates the specified value with the specified key in this map.
  • get(Object): Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
  • remove (Object key): Removes the mapping for a key from this map if it is present.
  • containsKey(Object key): Returns whether this map contains a mapping for the specified key.
  • containsValue(Object value): Returns whether this map maps one or more keys to the specified value.

This interface is also comprised of methods for bulk operations:

  • putAll(Map<? extends K,? extends V>): Copies all of the mappings from the specified map to this map.
  • clear(): Removes all of the mappings from this map.

In addition, the Map interface declares methods to get collection views on a map:

  • entrySet(): Returns a Set view of the mappings contained in this map.
  • keySet(): Returns a Set view of the keys contained in this map.
  • values(): Returns a Collection view of the values contained in this map.

7. What is serialization and how to implement it in the Java programming language?

Serialization in Java means converting an object’s state to a byte stream such that the byte stream can be reverted into a copy of the original object. A Java object is serializable if its class or any of its superclasses implements the Serializable interface. Serialization allows objects to be transferred across the network, persisted to a database, saved to a file and so on. Deserialization is the process of transforming the serialized byte stream back to an object.

  1. The Serializable interface

The Serializable interface enables an object to be serialized and implementing this interface is required for a class to be serializable. The Serializable interface, defining no fields and methods, only serves as a marker labeling classes as serializable.

If a class is serializable, all of its subclasses are serializable. This serializable class, however, may extend a non-serializable superclass. If this is the case, the non-serializable class must declare a no-argument constructor that is accessible to the serializable subclass. This requirement is important as such a no-constructor is used to initialize fields of the non-serializable superclass during deserialization.

In the process of serialization and deserialization, a serializable class is associated with a version number. This version number is used to verify that the classes at both sides of the process are compatible. Specifically, when an object is recreated from a byte stream, its blueprint class must match the class from which the original object is instantiated. The version number is essential in maintaining integrity when serialization and deserialization occur in different machines.

The version number of a serializable class can be explicitly set by declaring a static final field in the class itself as follows:

<access-modifier> static final long serialVersionUID = 2017L;

Any access modifier is allowable, but it is recommended to specify private to avoid accidental changes. The type of that field must be long.

If the version number field is not declared, the serialization runtime will calculate a default value based on various aspects of the serializable class. This computation is, however, dependent on the class definition in the class file, which may not be the same across compiler implementations. Therefore, to insure the consistency of a version number value, the serialVersionUID field should be explicitly defined and set.

Serialization is implemented by the ObjectOutputStream.writeObject(Object) method. The following code
fragment serializes a serializable Whizlabs instance to a file named “whizlabs.ser”:

Whizlabs origObject = // an instance of Whizalbs
FileOutputStream fos = new FileOutputStream(new File("whizlabs.ser"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(origObject);
Deserialization is done via the ObjectInputStream.readObject() method. The code snippet given below recreates an instance of Whizlabs from the "whizlabs.ser" file:
FileInputStream fis = new FileInputStream("whizlabs.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Whizlabs newObject = (Whizlabs) ois.readObject();
  1. Customizing serialization

If special handling is required during serialization and deserialization, methods with the exact signatures shown below should be declared within the serializable class:

private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException

For instance, objects of the following class print out messages when they are serialized and recreated from a byte stream:

class Whizlabs implements Serializable {

// declarations of fields and methods

private void writeObject(ObjectOutputStream oos) throws IOException {

System.out.println(“Serializing …”);

oos.defaultWriteObject();

}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {

System.out.println(“Deserializing …”);

ois.defaultReadObject();

}

}

  1. The Externalizable interface

The Externalizable interface is a subtype of Serializable. It defines two methods, namely writeExternal(ObjectOutput) and readExternal(ObjectInput). These methods give the serializable class complete control over the serialization and deserialization of an object and its supertypes. Methods of the Externalizable interface override the implementation customization via the writeObject and readObject methods described in the preceding section.

The following class declaration illustrates the use of the Externalizable interface:

class Whizlabs implements Externalizable {
    // declarations of fields and methods
@Override
public void writeExternal(ObjectOutput out) throws IOException {
    System.out.println("Serializing ...");
}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
     System.out.println("Deserializing ...");
}
}

When an object of the above class is serialized and deserialized, messages on the progress are printed out.

About Aditi Malhotra

Aditi Malhotra is the Content Marketing Manager at Whizlabs. Having a Master in Journalism and Mass Communication, she helps businesses stop playing around with Content Marketing and start seeing tangible ROI. A writer by day and a reader by night, she is a fine blend of both reality and fantasy. Apart from her professional commitments, she is also endearing to publish a book authored by her very soon.

Leave a Comment

Your email address will not be published. Required fields are marked *


Scroll to Top