First 10 of 50 Java Questions

Last evening I ran into the article Review these 50 questions to crack your Java programming interview. I am a firm believer that no matter which programming language you use, you will concentrate of the classes and methods you commonly use for work. I tend to concentrate on two to three languages (e.g., C/C++, Java and JavaScript) depending on what I am doing. For this reason it is good to refresh knowledge and who knows, learn something new, by going over the questions and attempting to answer them before looking at the suggested answer.

I will go over each question attempting to create my own response. I will then edit it accordingly to what javinpaul wrote in the original post on freeCodeCamp and Medium.

The questions in the original post start with number one (1). I looked at the first image on the post and decided to address it as question zero (0). I would assume that in the body of the original post that question will be addressed.

The output from a program I created to experiment and test concepts follow:

stringObjects <<<         s1.hashCode: -862545276
stringObjects <<< s1.identityHashCode: 1118140819   <====

stringObjects <<<         s2.hashCode: -862545276
stringObjects <<< s2.identityHashCode: 1975012498   <====

stringObjects <<< s1 == s2: false

stringObjects <<<         s3.hashCode: -862545276
stringObjects <<< s3.identityHashCode: 1975012498   <====

stringObjects <<< s2 == s3: true

main <<< String s1 = new String("Hello World") stringObjects: 2 main >>> n: 4
main <<< n: 4 is even
main <<< n: 4 is even
main <<< n: 4 is even
main <<< n: 4 is even

arrayListAndHashSet <<< al.size(): 11
arrayListAndHashSet <<<        al: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0]
arrayListAndHashSet <<<        al: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0]
arrayListAndHashSet <<<        al: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0]

arrayListAndHashSet <<< 3 found: true
arrayListAndHashSet <<< 9 found: false

arrayListAndHashSet <<< i % 5: 0 inserted
arrayListAndHashSet <<< i % 5: 1 inserted
arrayListAndHashSet <<< i % 5: 2 inserted
arrayListAndHashSet <<< i % 5: 3 inserted
arrayListAndHashSet <<< i % 5: 4 inserted
arrayListAndHashSet <<< hs.size(): 5
arrayListAndHashSet <<<        hs: [0, 1, 2, 3, 4]
arrayListAndHashSet <<<        hs: [0, 1, 2, 3, 4]

arrayListAndHashSet <<< 3 found: true
arrayListAndHashSet <<< 9 found: false

dclSingleton <<< w.str ==>class instantiated<==
dclSingleton <<< x.str ==>CLASS INSTANTIATED<==
dclSingleton <<< w.str ==>CLASS INSTANTIATED<==

threadSafeSingleton <<< w.str ==>class instantiated<==
threadSafeSingleton <<< x.str ==>CLASS INSTANTIATED<==
threadSafeSingleton <<< w.str ==>CLASS INSTANTIATED<==

volatileVariable <<< waiting...
run <<< count: 0
run <<< count: 1
run <<< count: 2
run <<< count: 3
run <<< count: 4
run <<< count: 5
run <<< count: 6
volatileVariable <<< awake!!!

transientTest <<< book serialized
transientTest <<< book deserialized
transientTest <<<    book: Book { ISBN=7007007, title=To Live and Let Die, author=Ian Fleming, edition=3 }
transientTest <<< oldBook: Book { ISBN=7007007, title=To Live and Let Die, author=Ian Fleming, edition=0 }

externalizableTest <<< book serialized
externalizableTest <<< book deserialized
externalizableTest <<<    book: Book { ISBN=7007007, title=To Live and Let Die, author=Ian Fleming, edition=3 }
externalizableTest <<< oldBook: Book { ISBN=7007007, title=To Live and Let Die, author=Ian Fleming, edition=3 }

I thought putting it after the code, but it made more sense to place it before so one can follow.

Q0. How many String Objects are created with the following code?

String s = new String("Hello World");

1) One
2) Two
3) Three

If you are not aware of the existence of the String Constant Pool (SCP) in Java, you would probably have selected answer one (1). The object is the one returned calling new String(“Hello World”). What happens is that the first time a constant is instantiated; an immutable object is also created and is left in the SCP. Next time the program makes a reference to the same literal, the object in the SCP is returned. Let’s look at the following code:

	/*
	 * How many String objects are created here?
	 * 
	 *  String s = new String("Hello World");
	 *  
	 *  1) One
	 *  2) Two
	 *  3) Three
	 */
	static int stringObjects() {
		
		// **** create initialized String instance ****
		String s1 = new String("Hello World");
		System.out.println("stringObjects <<<         s1.hashCode: " + s1.hashCode());
		System.out.println("stringObjects <<< s1.identityHashCode: " + System.identityHashCode(s1) + "\n");
		
		// **** String Constant Pool (SCP) ****
		String s2 = s1.intern();
		System.out.println("stringObjects <<<         s2.hashCode: " + s2.hashCode());
		System.out.println("stringObjects <<< s2.identityHashCode: " + System.identityHashCode(s2) + "\n");
		
		// **** false because they are two different objects ****
		System.out.println("stringObjects <<< s1 == s2: " + (s1 == s2) + "\n");
		
		// **** create a new string (uses SCP object) ****
		String s3 = "Hello World";
		System.out.println("stringObjects <<<         s3.hashCode: " + s3.hashCode());
		System.out.println("stringObjects <<< s3.identityHashCode: " + System.identityHashCode(s3) + "\n");
		
		// **** true because the objects are the same ****
		System.out.println("stringObjects <<< s2 == s3: " + (s2 == s3) + "\n");
		
		// **** generate count of objects ****
		int count = 0;
		if (s1 == s2)
			count = 1;
		else
			count = 2;
		
		if (s2 != s3)
			count++;
		
		// **** ****
		return count;
	}

Note that Java does not support the concept of pointers. The best we can do is to use the identity hash code. When we instantiate any of the strings (s1, s2 or s3) we get the same hash code. This is because the contents of the strings are the same. But when we look at the identity hash code, the first two string objects differ. From then on, any additional instantiation with the same string will return a copy of the string object in the SCP.

In conclusion, if the string is instantiated the first time, the JVM will create two string objects. From then on it will return a copy of the string object in the SCP.

Q1. How does Java achieve platform independence?

We need to understand the following concepts:

Java class file,

Java bytecode,

and the Java Virtual Machine (JVM).

When you compile a Java program it creates .class file which is collection of bytecodes, these bytecodes are not machine instructions (assembly language) instead they are instruction which the Java Virtual Machine (JVM) can understand and execute by converting them to assembly language for the hosting CPU.

In summary, the combination of the .class file, the bytecodes and JVM makes Java program platform independent.

Q2. What is ClassLoader in Java?

The Java Class Loader is a class which is used to load class files.

The ClassLoader works on the following three principles:

Delegation, visibility and uniqueness.

o Delegation principle forward request of class loading to parent class loader and only loads the class, if parent is not able to find or load class.

o Visibility principle allows child class loader to see all the classes loaded by parent ClassLoader, but parent class loader cannot see classes loaded by child.

o Uniqueness principle allows loading a class exactly once, which is basically achieved by delegation and ensures that child ClassLoader does not reload the class already loaded by the parent.

There are three default class loader used in Java:

Bootstrap, Extension, and System or Application class loaders.

o Bootstrap ClassLoader is responsible for loading standard JDK class files from rt.jar and it is parent of all class loaders in Java.

o Extension ClassLoader delegates class loading request to its parent, Bootstrap and if unsuccessful, loads class from jre/lib/ext directory or any other directory pointed by java.ext.dirs system property.

o System or Application ClassLoader is responsible for loading application specific classes from the CLASSPATH environment variable, -classpath or -cp command line option, Class-Path attribute of Manifest file inside JAR.

Except Bootstrap class loader, which is implemented in native language mostly in C, all Java class loaders are implemented using java.lang.ClassLoader abstract class.

Q3. Write a Java program to check if a number is Even or Odd?

There are different ways to determine if an integer is even or odd. The following code snippet illustrates some approaches.

		// **** Q3. Write a Java program to check if a number is Even or Odd? ****
		System.out.print("\nmain >>> n: ");
		int n = Integer.parseInt(sc.next());
		
		System.out.println("main <<< n: " + n + " is " + (((n % 2) == 0) ? "even" : "odd"));
		System.out.println("main <<< n: " + n + " is " + ((((n / 2) * 2) == n) ? "even" : "odd"));
		System.out.println("main <<< n: " + n + " is " + (((n & 1) == 0) ? "even" : "odd")); int ns = n >> 1;
		ns = ns << 1;
		System.out.println("main <<< n: " + n + " is " + ((n == ns) ? "even" : "odd"));

The first approach uses the modulus operator which is probably the most common approach. The second one uses a combination of dividing the number by 2 followed by multiplying it by 2. If the result matches the original number then the result is even.

The third approach uses the AND operator on the lowest bit of the number. If the bit is not set the number is even.

The fourth and final approach makes a copy of the value shifted one bit to the right. The second statement shifts the new value to the left. If the original integer matches the new value, then the original value is even. This approach is in essence the same as dividing by two and then multiplying by 2. CPUs use the shift operator on values in a register to avoid loading them from cache or main memory.

Q4. Differences between ArrayList and HashSet in Java?

Main difference between ArrayList and HashSet is that one is a List implementation while other is a Set implementation.

o List implementations are ordered, it store element in the order they were added, while Set implementation doesn’t provide such guarantee.

o Similarly, since List provides Random access, you can access any element directly if you know the index, but Set doesn’t provide such facility. You need to Iterate through whole collection to get access of any elements.

o Both ArrayList and HashSet are non synchronized collection class and not meant to be used in multi-threading and concurrent environment. You can make an ArrayList and HashSet synchronized by using Collections.synchroinzedCollection()

o Both ArrayList and HashSet can be traversed using Iterator.

o An Iterator of ArrayList and HashSet both are fail-fast, i.e. they will throw ConcurrentModificationException if the ArrayList or the HashSet is modified structurally once Iterator has been created.

o ArrayList allow duplicates while HashSet do not allow duplicates.

In the following code I experiment with the ArrayList and the HashSet classes.

	/*
	 * The main difference between ArrayList and HashSet is that 
	 * the first is a List implementation while the second is a Set implementation. 
	 */
	static void arrayListAndHashSet() {
		
		final int	MAX_ENTRIES	= 11;
		
		// **** instantiate an array list object ****
		ArrayList<Integer> al = new ArrayList<Integer>();
		
		// **** insert data into the array list ****
		for (int i = 0; i < MAX_ENTRIES; i++)
			al.add(i % 5);
		
		// **** display data ****
		System.out.println("arrayListAndHashSet <<< al.size(): " + al.size());
		System.out.println("arrayListAndHashSet <<<        al: " + al.toString());
		
		
		// **** iterate through the data ****
		int remove = 0;
		
		Iterator<Integer> it = al.iterator();
		System.out.print("arrayListAndHashSet <<<        al: [");
		
		try {
			while (it.hasNext()) {
				
				// **** ****
				int val = it.next();
				if (it.hasNext())
					System.out.print(val + ", ");
				else
					System.out.print(val);
				
//				// **** remove an element from the array list  ****
//				if (remove == 2) {
//					val = al.remove(2);
//					System.out.println("\narrayListAndHashSet <<< remove(4): " + val);
//				}
				
				// **** increment counter ****
				remove++;
			}
		} catch (ConcurrentModificationException e) {
			System.err.println("\narrayListAndHashSet <<< EXCEPTION message: " + e.toString());
		}
		System.out.println("]");

		// **** iterate through the data (index elements) ****
		System.out.print("arrayListAndHashSet <<<        al: [");
		for (int i = 0; i < al.size(); i++) {
			if (i < al.size() - 1)
				System.out.print(al.get(i) + ", ");
			else
				System.out.print(al.get(i));
		}
		System.out.println("]");
		System.out.println();
		
		// **** find an element ****
		Boolean found = al.contains(3);
		System.out.println("arrayListAndHashSet <<< 3 found: " + found);

		found = al.contains(9);
		System.out.println("arrayListAndHashSet <<< 9 found: " + found);
		System.out.println();

		
		// **** instantiate a hash set object ****
		HashSet<Integer> hs = new HashSet<Integer>();
		
		// **** insert data into the hash set (no duplicates) ****
		for (int i = 0; i < MAX_ENTRIES; i++)
			if (hs.add(i % 5))
				System.out.println("arrayListAndHashSet <<< i % 5: " + (i % 5) + " inserted");
		
		// **** display data ****
		System.out.println("arrayListAndHashSet <<< hs.size(): " + hs.size());
		System.out.println("arrayListAndHashSet <<<        hs: " + hs.toString());
		
		// **** iterate through the data ****
		remove = 0;

		it = hs.iterator();
		System.out.print("arrayListAndHashSet <<<        hs: [");
		
		try {
			while (it.hasNext()) {
				int val = it.next();
				if (it.hasNext())
					System.out.print(val + ", ");
				else
					System.out.print(val);
				
//				// **** remove an element from the hash set  ****
//				if (remove == 2) {
//					if (hs.remove(4))
//					System.out.println("\narrayListAndHashSet <<< remove(4): " + val);
//				}
				
				// **** increment counter ****
				remove++;
			}
		
		} catch (ConcurrentModificationException e) {
			System.err.println("\narrayListAndHashSet <<< EXCEPTION message: " + e.toString());
		}
					
		System.out.println("]");
		System.out.println();
		
		// **** find an element ****
		found = hs.contains(3);
		System.out.println("arrayListAndHashSet <<< 3 found: " + found);

		found = hs.contains(9);
		System.out.println("arrayListAndHashSet <<< 9 found: " + found);
		System.out.println();
	}

We start by instantiating an ArrayList object and populating it with repeated values. We verify that all values (including repeated ones) are in the array.

We then iterate through the array. Note the commented out code. It may be used to check what happens if the array is altered during iteration.

A second iteration over the elements is performed without the use of an Iterator. We just enumerate the values in the array.

Finally we find some elements in the array.

We now instantiate a HashSet object. We attempt to add the same values as we did for the array. We can see that the HashSet does not allow us to insert duplicate values.

We then iterate through the hash set. Note that the commented code may be used to check out what happens when the hash set is altered during iteration.

Finally we look for a couple values in the hash set.

Q5. What is double-checked locking in Singleton?

Double-checked locking is a software design pattern that is / was used to generate a Singleton. When using multi threading it does not work. Keep in mind that most production code is multi threaded.

I have written a couple blogs on the subject:  Singleton Threads and Singleton Pattern.

The following code illustrates a double-checked locking Singleton being called from a single thread. If interested you can look at the Singleton Threads post in which I create threads to check out a Singleton. In that case I used a semaphore to address the rush condition. We could also use the volatile decorator on the singleInstance object.

	/*
	 * double check locking in Singleton
	 * no longer needed after Java 1.5
	 */
	static void dclSingleton() {
		
		// **** instantiate the singleton ****
		DCLSingleton w = DCLSingleton.getSingleInstance();
		System.out.println("dclSingleton <<< w.str ==>" + w.str + "<==");
		
		// ***** instantiate the singleton ****
		DCLSingleton x = DCLSingleton.getSingleInstance();
		
		// **** alter the string ****
		x.str = x.str.toUpperCase();
		System.out.println("dclSingleton <<< x.str ==>" + x.str + "<==");
		
		// **** w and x refer to the same instance ****
		System.out.println("dclSingleton <<< w.str ==>" + w.str + "<==");
		System.out.println();
	}

The code for the DCLSingleton class follows:

package questions.canessa.com;

/*
 * Double Check Locking Singleton class
 */
public class DCLSingleton {

	// **** without volatile it will NOT be thread-safe 
	//		write to a volatile field will happen before any read, 
	//		which negates the possibility of seeing a half initialized 
	//		instance of Singleton class ****
	private static volatile DCLSingleton singleInstance = null;
	
	// **** for testing purpose ****
	public 	String str = "class instantiated";
	
	// **** constructor made private to disable instance creation outside of this class ****
	private DCLSingleton() {
	}
	
	// **** creates an instance of this class ****
	public static DCLSingleton getSingleInstance() {
		
		// **** 1st check non-synchronized ****
		if (singleInstance == null) {
			
			synchronized (DCLSingleton.class) {
				
				// **** 2nd check synchronized (only executes one time during life cycle of the Singleton) ****
				if (singleInstance == null) {
					singleInstance = new DCLSingleton();
				}
			} 
		}
		
		// **** return the single instance ****
		return singleInstance;
	}

}

The code uses volatile decorator. We could also have initialized the singleInstance so the ClassLoader initializes the variable and there is no rush condition to contend with. This is just one more example that shows that experience and testing are of extreme importance when developing robust applications and services.

Q6. How do you create thread-safe Singleton?

The following code show calling a thread-safe Singleton implementation. As I mentioned before in this post, I got lazy and did not go through the trouble of testing the Singleton with multiple threads. I urge you to check my previous post Singleton Threads and use it against this implementation.

	/*
	 * Thread safe singleton
	 */
	static void threadSafeSingleton() {
		
		// **** instantiate the singleton ****
		Singleton w = Singleton.getSingleInstance();
		System.out.println("threadSafeSingleton <<< w.str ==>" + w.str + "<==");
		
		// ***** instantiate the singleton ****
		Singleton x = Singleton.getSingleInstance();
		
		// **** alter the string ****
		x.str = x.str.toUpperCase();
		System.out.println("threadSafeSingleton <<< x.str ==>" + x.str + "<==");
		
		// **** w and x refer to the same instance ****
		System.out.println("threadSafeSingleton <<< w.str ==>" + w.str + "<==");
		System.out.println();
	}

The actual implementation for the Singleton follows:

package questions.canessa.com;

/*
 * 
 */
public class Singleton {

	// **** for testing purpose ****
	public String str = "class instantiated";
	
	// **** created by ClassLoader ****
	private static final Singleton singleInstance = new Singleton();
	
	// **** constructor made private to disable instance creation outside of this class ****
	private Singleton() {
	}
	
	// **** returns the single instance of this class ****
	public static Singleton getSingleInstance() {
		return singleInstance;
	}
}

Note that the singleInstance is created by the ClassLoader. This eliminates rush conditions because the instance will be created before the code executes. This warranties that the getSingletonInstance() method will always return the same object.

Q7. When to use the volatile (synchronization) variable?

In this case we use the volatile decorator for synchronization. Following is the code we use to test volatile with a thread.

	/*
	 * Volatile variable.
	 * Volatile has semantics for memory visibility.
	 * Basically, the value of a volatile field becomes visible to all readers 
	 * (other threads in particular) after a write operation completes on it. 
	 * Without volatile, readers could see some non-updated value.
	 */
	static void volatileVariable() {
		
		// Ensures that the value should always be read from main memory and a thread 
		// should not use the cached value of that variable from their own stack.
		
		// **** create and start my thread ****
		MyThread myThread = new MyThread();
		myThread.start();
		
		// **** wait for a few seconds ****
		try {
			System.out.println("volatileVariable <<< waiting...");
			TimeUnit.SECONDS.sleep(5);
			System.out.println("volatileVariable <<< awake!!!");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		// **** stop the thread ****
		myThread.die();
	}

In this simple code we create a thread and start it. After a few seconds we stop it by calling the die() method.

The code for the thread follows:

package questions.canessa.com;

import java.util.concurrent.TimeUnit;

/*
 * 
 */
public class MyThread extends Thread {

	// **** flag used to stop this thread ****
	private volatile boolean stop = false;
	
	// **** ****
	public void run() {
		
		// **** counter ****
		int count = 0;
		
		// **** loop until told to stop ****
		while (!stop) {
			System.out.println("run <<< count: " + count);
			count++;
			try {
				TimeUnit.MILLISECONDS.sleep(789);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	// **** flag to kill this thread ****
	public void die() {
		stop = true;
	}
}

Note that the stop variable is declared with the volatile decorator and is initialized to false. When the die() method is invoked, the value is set to false causing the thread to exit.

Q8. When to use a transient variable in Serialization?

A transient variable is a variable whose value is not serialized during Serialization and which is initialized by its default value during de-serialization, for example for object transient variable it would be null. This behavior can be customized by using the Externalizable interface. You can read more about transient here.

A transient variable is used to prevent any object from being serialized and you can make any variable transient by using transient keyword as illustrated in the Book class:

package questions.canessa.com;

import java.io.Serializable;

/*
 * 
 */
public class Book implements Serializable {

	// **** ****
	private static final long serialVersionUID = 1L;
	
	// **** ****
	private int 			ISBN;
	private String 			title;
	private String 			author;
//	private int 			edition = 1;
	private transient int 	edition = 1;

	
	// **** constructor ****
	public Book (int ISBN, String title, String author, int edition) {
		this.ISBN 		= ISBN;
		this.title 		= title;
		this.author 	= author;
		this.edition	= edition;
	}
	
	// **** constructor ****
	public Book (int ISBN, String title, String author) {
		this.ISBN 		= ISBN;
		this.title 		= title;
		this.author 	= author;
	}
	
	// **** to string ****
	public String toString() {
		return "Book { ISBN=" + ISBN + ", title=" + title + ", author=" + author + ", edition=" + edition + " }";
	}
}

In this code the edition variable has been decorated with the transient keyword.

The following code illustrates how an instance of the class Book is instantiated, serialized into a file, and deserialized from the specified file. If you try the code with and without the transient keyword in the variable edition you will see the difference. Without transient the value is stored or returned. With the transient keyword present the value is not stored.

	static void transientTest() {
		
		// **** ****
		Book oldBook = null;
		
		// **** create a book ****
		Book book = new Book(7007007, "To Live and Let Die", "Ian Fleming", 3);
//		Book book = new Book(7007007, "To Live and Let Die", "Ian Fleming");
		
		// **** ****
		final String fileName = "c:\\temp\\book.ser";
		
		// **** ****
		try {
			
			// **** serialize ****
			FileOutputStream fos 	= new FileOutputStream(fileName);
	        ObjectOutputStream oos 	= new ObjectOutputStream(fos);
			oos.writeObject(book);
			
			// **** close streams ****
			oos.close();
			fos.close();
			
			// ???? ????
			System.out.println("transientTest <<< book serialized");
			
			// **** deserialize ****
            FileInputStream fis 	= new FileInputStream(fileName);
            ObjectInputStream ois 	= new ObjectInputStream(fis);
            oldBook = (Book)ois.readObject();
            
            // **** close streams ****
            ois.close();
            fis.close();

			// ???? ????
			System.out.println("transientTest <<< book deserialized"); 
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// **** display books ****
		System.out.println("transientTest <<<    book: " + book.toString());
		System.out.println("transientTest <<< oldBook: " + oldBook.toString());
	}

Q9. What is the difference between a transient and volatile variable?

Both transient and volatile modifiers are completely different to each other.

Main difference between a transient and a volatile variable is that transient variables are not serialized during Serialization process while volatile variables are used to provide alternative synchronization in Java.

Using volatile keyword or modifier on fields, signals the compiler that this variable is accessed by multiple threads and any reordering or optimization by compiler should be avoided.

Q10. What is the difference between Serializable and Externalizable in Java?

a) One of the obvious differences between Serializable and Externalizable is that Serializable is a marker interface i.e. does not contain any methods but Externalizable interface contains two methods writeExternal() and readExternal().

b) The second difference between Serializable an Externalizable is responsibility of Serialization. When a class implements the Serializable interface, a default Serialization process gets kicked off and that takes responsibility of serializing the super class state.

When any class in Java implement java.io.Externalizable it is the responsibility of the code to implement the Serialization process i.e. preserving all important information.

c) You cannot do much to improve performance of the default serialization process except reducing number of fields to be serialized by using transient and static keywords but with Externalizable interface you have full control over Serialization process.

d) Another important difference between Serializable and Externalizable interface is maintenance. When your class implements Serializable, the interface it is tied with default representation which is fragile and easily breakable if the structure of the class changes e.g. adding or removing field(s).

By using java.io.Externalizable interface you can create your own custom binary format for your object.

package questions.canessa.com;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

/*
 * 
 */
public class ExtBook implements Externalizable {

	// **** ****
	private int 	ISBN;
	private String 	title;
	private String 	author;
	private int 	edition;
	
	// **** constructor ****
	public ExtBook (int ISBN, String title, String author, int edition) {
		this.ISBN 		= ISBN;
		this.title 		= title;
		this.author 	= author;
		this.edition	= edition;
	}
	
	// **** no argument constructor ****
	public ExtBook() {
	}

	// **** write object ****
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeInt(ISBN);
		out.writeObject(title);
		out.writeObject(author);
		out.writeInt(edition);
	}

	// **** read object ****
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		ISBN 	= in.readInt();
		title 	= (String)in.readObject();
		author 	= (String)in.readObject();
		edition	= in.readInt();
	}
	
	// **** to string ****
	public String toString() {
		return "Book { ISBN=" + ISBN + ", title=" + title + ", author=" + author + ", edition=" + edition + " }";
	}
}

The ExtBook class closely mimics the Book class from a previous question. The main difference is that in this case we are implementing Externalizable interface. Because of this, we need to at least to implement the two required methods writeExternal() and readExternal(). The responsibility of serializing out and in falls on this code.

	/*
	 * Externalizable test.
	 * 
	 * When an Externalizable object is reconstructed, 
	 * an instance is created first using the public no-argument constructor, 
	 * then readExternal method is called.
	 * 
	 * When an object implements the Serializable interface, 
	 * no constructor is called and hence any initialization 
	 * which is implemented in constructor can not be done.
	 */
	static void externalizableTest() {
		
		// **** ****
		ExtBook oldBook = null;
		
		// **** create a book ****
		ExtBook book = new ExtBook(7007007, "To Live and Let Die", "Ian Fleming", 3);
		
		// **** name for file ****
		final String fileName = "c:\\temp\\book.ext";
		
		// **** serialize book ****
		try {
			FileOutputStream fos 	= new FileOutputStream(fileName);
			ObjectOutputStream oos 	= new ObjectOutputStream(fos);
			oos.writeObject(book);
			oos.flush();
			
			// **** close streams ****
			oos.close();
			fos.close();
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("externalizableTest <<< EXCEPTION serializing");
			System.exit(-1);
		}
		
		// ???? ????
		System.out.println("externalizableTest <<< book serialized");
					
		// **** deserialize book ****
		try {
			
            FileInputStream fis 	= new FileInputStream(fileName);
            ObjectInputStream ois 	= new ObjectInputStream(fis);
            oldBook = (ExtBook)ois.readObject();
            
            // **** close streams ****
            ois.close();
            fis.close();
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("externalizableTest <<< EXCEPTION deserializing");
			System.exit(-1);
		}
		
		// ???? ????
		System.out.println("externalizableTest <<< book deserialized"); 

		// **** display books ****
		System.out.println("externalizableTest <<<    book: " + book.toString());
		System.out.println("externalizableTest <<< oldBook: " + oldBook.toString());
	}

This last function illustrates side by side the use of automatic serialization using the Book class and Externalizable by using the ExtBook class.

If you are interested in experimenting with this code, I have placed it in my GitHub repository.

If you have comments or questions regarding this or any other post in this blog, or if you would like me to help with any phase in the SDLC (Software Development Life Cycle) of a product or service, please do not hesitate and leave me a note below. Requests for help will remain private.

Keep on reading and experimenting. It is the best way to learn!

John

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.