Java Additional Topics Tutorial

Welcome to the Java Additional Topics tutorial offered by Simplilearn. The tutorial is a part of the Java Certification Training Course.

Let us begin with the objectives of this tutorial in the next section.


Let us now see the topics covered in this Java additional topics tutorial -

  • Explain Inner Classes and Inner Interfaces

  • Define String API

  • Define Thread

  • Determine Collection Framework

  • Explain Comparable Comparator and other Functional Interfaces

  • Identify File Handling and Serialization

Let us learn the Java inner classes and inner interfaces in the next section

Java Inner Class and Inner Interface

Let us now learn about the java inner class.

Java Inner Class

The inner class is a class declared inside the class or interface that can access all the members of the outer class, including private data members and methods.

class Outer_class

{                                     //code


class Inner_class{         //code




The code snippet above gives the scope of the outer class. Within the outer class, we have defined an inner class.

The advantages of doing so include -

  • Accessible to all data members and methods of the outer class, including private data members and methods.

  • It develops a more maintainable and readable code.

  • And it requires less coding effort. In our classes are also referred to as nested classes.

Java Interface - Inner

Inner interface, also known as the nested interface, declares an interface inside another interface.

public interface Map


interface Entry{

int getKey();


void clear();


Here we observe the outer loop of the interface and within it, we have in an interface that has been declared.

The advantages include -

  • It can be used to group interfaces together.

  • Encapsulation can be done using inner interfaces

  • Interfaces are used to maintain and read codes better.

In the next section, we will learn about the Java threads.

You too can join the high earne’s club. Enroll in our Java Certification Course and join the high earner’s club.

Java Threads

A thread is an independent part of the execution within a program. The java.lang.Thread class enables you to create and control threads. A Java thread is composed of three main parts.

They are -

  • The virtual CPU,

  • The code that the CPU executes, and

  • The data on which the code works.

All our systems have a processor, and ideally, then you run the public static void main program, You are typically running a single process on top of your processor. Although, your processor has much more capability.

Whenever the processor has some spare time, we can start parallel processes that run in parallel within the main thread so that we can utilize the time of the processor to effectively perform some computational action.

Hence, within the main thread that is already running, we can start parallel child processes or child threads that are running so that when the processor has some idle time, we can effectively utilize that idle time to do parallel processing and have multiple threads running in parallel.

Each thread can then be assigned a work, which could be a function to execute or some code block to execute.

Creating a Java Thread

A Java thread can be created by the thread class or by implementing Runnable Interface. Commonly used constructors of the Java thread class include -

  • Thread()

  • Thread(String name)

  • Thread(Runnable r)

  • Thread(Runnable r, String name)

They accept either a string name of the thread or a runnable object or both. To create a thread, the java.lang.The Runnable interface is preferred over inheriting from java.lang.Thread. Since Java doesn't support multiple inheritances, the extended Java thread class will lose the chance to further extend or inherit other classes.

Implementing Runnable interface will make the code easily maintainable as it does a logical separation of the task from the runner. In object-oriented programming, extending a class means modifying or improving the existing class, implementing Runnable is hence a good practice.

There are two ways in which we could create a thread, one of them is where you can go ahead and inherit the thread class, in which case you cannot further inherit from any other class as java follows the single inheritance. The other way which is also a better way is to implement the Runnable interface, and then you can go ahead and inherit from a class of your choice in parallel.

In the next section, let us look at an example of creating a Java Thread.

Example of Creating a Java Thread

Let us look at a simple example of creating a thread. Here, we have a class called ThreadTester and HelloRunner. The HelloRunner class implements the Runnable interface. Run method is the method that will be called the moment we start the thread and this method has been defined inside the Runnable interface.

public class ThreadTester


public static void main(String args[ ]) {

HelloRunner r = new HelloRunner();

Thread t = new Thread(r);




class HelloRunner implements Runnable {

int i;

public void run() {


while (true) {

System.out.println(“Hello” + i++);

if(i==50) {






In the next section, we will look at synchronizing a Java thread.

Synchronizing a Java Thread

Synchronized keyword enables a programmer to control threads that are sharing data. Synchronization can be done in two ways -

  • It can be applied before a thread.

  • It can be applied after the trip.

Every object is associated with a flag called object lock flag, and this flag is enabled by a synchronized keyword.

Let us now look at Applying synchronized before a thread and Applying synchronized after a thread.

Applying synchronized in Java before a thread:

To apply synchronized before a thread. When a thread reaches the synchronized statement, it examines the object, passed as the argument, and tries to obtain the lock flag from that object before continuing to the next step.

Here, the moment this particular code block is accessed, the thread simply tries to acquire a lock over this block of code and then proceeds with processing this code block.

Applying synchronized in Java after a thread:

In this example, we see that the synchronization is applied after a thread. That is where it will try to acquire a lock on this particular block of code. This is an example of the applying or acquiring a synchronized lock after the thread.

In the next section, we will learn about Java Deadlock.

Java Deadlock

Deadlock is a part of the Java multithreading. It occurs when a thread is waiting for an object lock that is held by another thread. But, the other thread is waiting for an object lock that has already been held by the first thread.

Let us understand deadlock with an example.

public class TestThread


public static Object Lock1 = new Object();

public static Object Lock2 = new Object();

public static void main(String args[]) {

Thread1 T1 = new Thread1();

Thread2 T2 = new Thread2();


T2.start(); }

private static class Thread1 extends Thread {

public void run() {

synchronized (Lock1) {

System.out.println("Thread 1: Hold lock 1...");

try { Thread.sleep(15); }

catch (InterruptedException e) {}

System.out.println("Thread 1: Wait lock 2...");

synchronized (Lock2) {

System.out.println("Thread 1: hold lock 1 & 2...");





private static class Thread2 extends Thread


public void run() {

synchronized (Lock2) {

System.out.println("Thread 2: Hold lock 2...");

try { Thread.sleep(10); }

catch (InterruptedException e) {}

System.out.println("Thread 2: Wait for lock 1...");

synchronized (Lock1) {

System.out.println("Thread 2: Hold lock 1 &








Thread 1: Hold lock 1…

Thread 2: Hold lock 2…

Thread 1: Wait for lock 1…

Thread 2: Wait for lock 2…

In the next section, we will look at the Java collection framework.

Java Collection Framework

This is part of the java.util.Collections package.


The java.util.Collections class consists exclusively of static methods that operate on or return collections. It contains polymorphic algorithms that operate on collections, "wrappers" (which return a new collection backed by a specified collection), and a few other odds and ends.

The methods of this class all throw a NullPointerException if the collections or class objects provided to them are null.

The fields for java.util.Collections class:

Modifier and Type

Field and Description

static List EMPTY_LIST

This is the empty list.

static Set EMPTY_SET

This is the empty set.

static Map EMPTY_MAP

This is the empty map

Java Collection Implementation

There are several purposes of implementation of core interfaces (Set, List, and Map) available as part of the Collection framework. This allows us to store information in memory which could be without a restriction often array where an array has a fixed size whereas collections do not have a fixed size.


Hash Table

Resizable Array

Balanced Tree

Linked List

Hash Table + Linked List



















General Purpose Collection Implementations

Java Collection Implementation Example—HashSet in Set

Let us now look at an example of the HashSet.

public class HashSetExample {

public static void main(String args[ ]) {

// HashSet declaration

Set<String> hset = new HashSet<String>();

// Adding elements to the HashSet






//Addition of duplicate elements



//Addition of null values



//Displaying HashSet elements




The declaration of the HashSet is specialized to the type string, so it will only accept the string type. We then add a few values to the HashSet. We know that, collections also accept duplicate values, therefore, in our code we are adding two duplicate values. We can also add null values as collections accept reference types and reference types are allowed to point to nothing.

Collection Implementation Example—ArrayList in List

This is an example of the ArrayList.

public class ArrayListExample {

public static void main(String args[ ]) {

/*Creation of ArrayList: add String elements so I made it of string type */

List<String> obj = new ArrayList<String>();

/*This is how elements should be added to the array list*/






/* Displaying array list elements */

System.out.println("Currently the array list has following elements:"+obj);

/*Add element at the given index*/

obj.add(0, "Rahul");

obj.add(1, "Justin");



Here, we create a List object and specialize it to the ArrayList with a generic implementation of the type string which mentions that it can take only strings. We then add a few string values and then we can prit out the entire list.

Collection Implementation Example—HashMap in Map

Let us now look at the next collection which is the HashMap. Let us look at an example where at the start of the program, we are creating an object of the map. The map takes, a key and a pair value. We are specializing this to an integer so that all key should be of the type INT and all the values to the corresponding key should be of the type string.

public class Details


public static void main(String args[ ]) {

/* This is how to declare HashMap */

Map<Integer, String> hmap = new HashMap<Integer, String>();

/*Adding elements to HashMap*/

hmap.put(1, “Sam");

hmap.put(2, "Rahul");

hmap.put(3, "Singh");

hmap.put(9, "Ajeet");

hmap.put(14, "Anuj");

/* Display contentCollection Implementation Example using Iterator*/ —HashMap in Map

Iterator<Map.Entry<Integer,String>> it= hmap.entrySet().iterator();



System.out.print("key is: "+ e.getKey() + " & Value is: ");



/* Get values based on key*/

String var= hmap.get(2);

System.out.println("Value at index 2 is: "+var)



Hierarchy of Java Collection Framework

This is the hierarchy of the collection framework. These are the interfaces that are available. There are three interfaces that inherit from Java collection, which is:

  • the list,

  • queue,

  • set

In terms of the list, we have ArrayList that we've used earlier; in terms of queues, we have linked list in PriorityQueue, which is implemented as a queue. In terms of sets, which hold the key pair values, as we've seen in the HashSet.

Hierarchy of Collection—Map

In terms of the map, we have HashMap, HashTable, SortedMap, and TreeSet. So these are the set off collection classes that also provide, the hash table or a key pair value storage mechanism.

Java Comparable

The comparable interface is a member of the “java.lang” package. By implementing the Java comparable interface, you can provide order to objects of any class. You can also sort collections that contain objects of classes that implement the Comparable interface.

Some Java classes that can implement the comparable interface are Byte, Long, String, Date, and Float. To write custom comparable types, you need to implement the compareTo method of the Comparable interface.

The Comparable interface can be used to implement a compareTo method. This compareTo method can be used specifically when we are sorting custom objects. We can use it to evaluate whether the value of one object is larger than the other object and accordingly decide how to sort those objects.

In the next section, we will learn about the Java comparator.

Java Comparator

The comparator interface is used to order the objects of the user-defined class. For example, consider the Student class described previously; the sorting of students was restricted to sorting on GPAs. A comparator object is capable of comparing two objects of two different classes.

In the next section, we will look at the Java comparable and comparator example.

Comparable and Comparator Example

Let us see an example. We are creating a new package called comparable comparator demo. We create a class called bus that implements the comparable interface. The comparable interface is specialized to the type Bus.

We have getter and the setter for the bus id and the bus name to retreive and store values. We also have a getter and setter for the fare and also for the ratings.

package comparable_comparator_demo;

public class Bus implements Comparable<Bus>{

private Integer busId;

private String busName;

private Double fare;

private Double ratings;

public Integer getBusId() {

return busId;


public void setBusId(Integer busId) {

this.busId = busId;


public String getBusName() {

return busName;


public void setBusName(String busName) {

this.busName = busName;


public Double getFare() {

return fare;


public void setFare(Double fare) {

this.fare = fare;


public Double getRatings() {

return ratings;


public void setRatings(Double ratings) {

this.ratings = ratings;



public String toString() {

return "Bus [busId=" + busId + ", busName="

+ busName + ", fare="

+ fare + ", ratings=" +

ratings + "]";


public Bus() {


public Bus(Integer busId, String busName, Double fare, Double

ratings) {


this.busId = busId;

this.busName = busName;

this.fare = fare;

this.ratings = ratings;



public int compareTo(Bus o) {

// TODO Auto-generated method stub

return o.busId.compareTo(this.busId);



From the above example, we will now create the main class:

package comparable_comparator_demo;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

public class BusMain {

public static void main(String[] args) {

// TODO Auto-generated method stub

Bus b1 = new Bus(1000, "Wipro Travels", 1500.50d, 4.8d);

Bus b2 = new Bus(1200, "Java Travels", 1200.50d, 3.8d);

Bus b3 = new Bus(1100, "J2EE Travels", 1750.50d, 4.9d);

Bus b4 = new Bus(1010, "JME Travels", 1250.50d, 2.1d);

Bus b5 = new Bus(1001, "List Travels", 1100.50d, 3.2d);

Bus b6 = new Bus(1900, "WOOW Travels", 1800.50d, 4.1d);

List<Bus> busList = new ArrayList<>();








System.out.println("Printing all the buses");

for (int i = 0; i < busList.size(); i++) {





Collections.sort(busList, new FareComparator());

System.out.println("Printing all the buses sorted

based on Fare");

for (int i = 0; i < busList.size(); i++) {





Collections.sort(busList, new RatingComparator());

System.out.println("Printing all the buses sorted

based on Ratings");

for (int i = 0; i < busList.size(); i++) {



System.out.println("Printing busses using


for(Bus b:busList){





Rate Comparator syntax from the example:

package comparable_comparator_demo;

import java.util.Comparator;

public class RatingComparator implements Comparator<Bus> {


public int compare(Bus o1, Bus o2) {

// TODO Auto-generated method stub





Fare Comparator syntax from the example:

package comparable_comparator_demo;

import java.util.Comparator;

public class FareComparator implements Comparator<Bus> {


public int compare(Bus o1, Bus o2) {

// TODO Auto-generated method stub

return o1.getFare().compareTo(o2.getFare());



Java Iterator

Iterator is used for looping through various collection classes such as HashMap, ArrayList, LinkedList, and so on. It is used to traverse collection object elements one by one. It is applicable for all Collection classes. Therefore, it is also known as Universal Java Cursor. It supports both READ and REMOVE Operations.

The syntax of Iterator:

Iterator<E> iterator()

Iterator Example

Let us now look at an example for the iterator. Here, we are importing three packages, the iterator package, the LinkedList package, and the List package. We create an object of the type LinkedList and add three names to it, John, James, and Joseph.

import java.util.Iterator;

import java.util.LinkedList;

import java.util.List;

public class ExternalIteratorDemo


public static void main(String[] args)


List<String> names = new LinkedList<>();



names.add(“Joseph"); // Getting Iterator

Iterator<String> namesIterator = names.iterator();

while(namesIterator.hasNext()){ // Traversing elements





Java Iterator For-Each Loop

The For-each loop in Java is used to make the code clearly readable and also eliminates the programming errors.

The Syntax for Iterator For-each Loop is given by :  

for(data_type variable : array | collection){ }

Example for using the for-each loop:

Let us now look at an example of the for-each loop. We have a simple integer array that takes four elements.

class ForEachExample1


public static void main(String args[ ]){

int arr[]={17,26,65,24};

for(int i:arr){










Java File Handling

The various Java file handling methods include -

  • Creating File objects

  • Manipulating File objects

  • Reading and Writing file streaming

File handling is saving data by creating a file on your file system. The file could be in the form of a text, a binary, or an XML. You can also manipulate those files by opening them, deleting them, inserting data, appending data to the file etc.

While reading and writing, we make use of the file stream. We can save all the data in the file stream and simply flush that data at one shot into the file. Hence, instead of having three round trips over the network, we will have a single round trip to push data into the file.

This is what is meant by file streaming. It is a temporary storage area, therefore, it is not required to keep doing I/O operations. I/O operations or reading and writing to the file frequently is expensive from a performance perspective and will hence degrade the performance of the application. Thus, the streaming feature prevents performance degradation.

Creating a New File Object in Java

The File class provides several utilities for handling files and obtaining information about them. You can create a File object that represents a directory and then use it to identify other files.

File myFile;

myFile = new File (“myfile.txt”);

myFile = new File (“MyDocs”, “myfile.txt”);

File myDir = new File (“MyDocs”);

myFile = new File (myDir, “myfile.txt”);

Manipulating File Object

After creating a File object, we can use one of the following methods to gather information about the file.

File Names:

The following are the methods that return file names:

String getName()

String getPath()

String getAbsolutePath()

String getParent()

boolean renameTo (File newname)

General File Information and Utilities:

The following methods return General File Information:

long lastModified()

long length()

boolean delete()

Directory Utilities:

The following methods will provide directory utilities:

boolean mkdir()

String[ ] list()

File Tests:

The following methods will provide information about file


boolean exists()

boolean canWrite()

boolean canRead()

boolean isFile()

boolean is Directory()

boolean isAbsolute()

boolean isHidden()

Reading from a File

In Java, we can use to read content from a file.


public class ReadFile


public static void main (String [ ] args) {

File = new File (args[0]);

try {

        //Create a buffered reader

        // to read each line from a file.

BufferedREader in = new BufferedReader (new FileReader(file));

String s;

Writing from a File

To write a file we make use of the following method:

Files.write(Paths.get(fileName), content.getBytes(), StandardOpenOption.CREATE);

This is explained with an example below using the try-catch blocks.



// Read each line from the file

s = in.readline();

while (s != null) {

System.out.println(“Read: “ + s);

s = in.readline();


} finally {

// Close the buffered reader




catch (FileNotFOundException e1)


// If this file does not exist

System.err.println(“File not found: “ +


s = in.readline();


catch (IOException e2) {

// Catch any other IO exceptions.






Serialization is a mechanism for saving the objects as a sequence of bytes and rebuilding the

byte sequence back into a copy of the object later. For a class to be serialized, the class must implement the interface.

The Serializable interface has no methods and only serves as a marker that indicates that the

class that implements the interface can be considered for serialization. The difference between the and serialization is that in we simply take data and save it to the file, whereas in serialization, we are actually saving an object.

Serialization stores data along with structure and hence we can serialize an entire object to a file. Thus, we can retrieve both the state and structure of the object and this process is called as deserialization.

Java Certification Training caught your attention? Check out our course preview now!

Serialization and Object Graph

When a field is a reference to an object, the fields of that referenced object are also serialized if

that object’s class is serializable. The tree or structure of an object’s fields, including these sub-objects, constitutes the object graph.

If the object graph contains a non-serializable object reference, the object can still be serialized

if the reference is marked with the transient keyword.

public class MyClass implements Serializable


public transient Thread myThread;

private String customerID

private int total;

Serialization Example

From this example let us learn how exactly serialization is done.

Writing to an object Stream

Here, we are creating a new Date object and we are putting a try block because it helps in saving data to a file and there is a high probability that there could be an exception. We create an output stream giving the name of date.serialization (date.ser), then we create an object output stream and passing it with a reference of f; where, f is the name of the file to save.   

public class SerializeDate


SerializeDate () {

Date d = new Date ();

try {

FileOutputStream f = newFileOutputStream (“date.ser”);

ObjectOutputStream s = new ObjectOutputStream (f);

s.writeObject (d); // Serialization starts here

s.close ();

} catch (IOException e) {

e.printStackTrace ();



public static void main (String args[]) {

new SerializeDate ();



Reading from an object Stream

After serializing the data from the previous example, we will now look at deserializing the data and converting it from the data we have saved in the file to an object. Thus, we create a date object and assign it to null.

We create an object input stream object and pass it a reference of file f. We then create, s.readObject which means it will read the object from the file called date.ser then we forcibly do a type cast to the type date and store that data in d.

public class DeSerializeDate


DeSerializeDate () {

Date d = null;

try {

FileInputStream f = newFileInputStream (“date.ser”);

ObjectInputStream s = new ObjectInputStream (f);

d = (Date) s.readObject ();// Serialization starts here

s.close ();

} catch (Exception e) {

e.printStackTrace ();


System.out.println(“Deserialized Date object from date.ser”);

System.out.println(“Date: ” +d);


public static void main (String args[]) {

new DeSerializeDate ();




Let us summarize what we have learned in this java additional topics tutorial :

  • Java inner class or nested class is a class that is declared inside the class or interface.

  • There are several purposes of implementations of core interfaces (Set, List, and

  • Map) in Collection framework.

  • A thread is an independent path of execution within a program.

  • By implementing the Comparable interface, you can provide order to the objects of any class. The Comparator interface provides greater flexibility with ordering.

  • Serialization is a mechanism for saving the objects as a sequence of bytes and rebuilding the byte sequence back into a copy of the object later.


Thus, we come to an end to the Java Additional Topics Tutorial.

  • Disclaimer
  • PMP, PMI, PMBOK, CAPM, PgMP, PfMP, ACP, PBA, RMP, SP, and OPM3 are registered marks of the Project Management Institute, Inc.

Request more information

For individuals
For business
Phone Number*
Your Message (Optional)
We are looking into your query.
Our consultants will get in touch with you soon.

A Simplilearn representative will get back to you in one business day.

First Name*
Last Name*
Work Email*
Phone Number*
Job Title*