Abstraction in Java

Abstraction in Java

If you have started to learn Java then I believe you must have somewhere come across term called object-oriented programming or OOPs concept. Now there are four pillars in Oops i.e., Abstraction, polymorphism, encapsulation and inheritance. In this article we will discuss about one of the four pillars of Oops i.e., Abstraction.

Abstraction basically is the art of hiding implementation details from user and provide the user what they want. Let’s try to understand with real world example. Most of us are quite fond of owning a car. When we go to place order for the car we are really not interested to understand very fine details of implementation of each and every component insider the car engine, Gear box etc., we leave those technical details and implementation for manufacturing engineers and mechanics to understand we are simply interested in the car so does the manufacturing company. They are interested to exactly provide us what we want and hide the fine implementation details from us. Likewise, there are tons of real-world examples where abstraction is in play whether smartphone you are using or smart television you are watching all have implemented abstraction in one way or the other.

Coming back to Java programming or any object-oriented programming to be more precise same principle follows code’s implementation details are hidden and only the necessary functionality is provided to the user. There are two ways to achieve abstraction in java: –

  1. By using interfaces
  2. By using abstract classes

Interfaces- Consider a television remote which only contains functionality to operate a television and it doesn’t serve any other purpose besides operating the television. You won’t be able to operate a refrigerator with a television remote. Here remote acts as an interface between you and the television. It contains all the necessary functionalities which you require while hiding the implementation details from you. In java Interfaces are similar to classes except they contain empty methods and can contain variables also. By empty methods it means that they don’t provide any implementation details and its for the classes or clients to provide the necessary implementation details for that method (or methods) when they implement the interface.

Syntax :-

public interface XYZ {

public void method ();


Example :

public interface TelevisionRemote {
public void turnOnTelevision();

public void turnOffTelevision();


A java class can just use the implements keyword to implement the interface and provide the implementation of the methods of the interface.

Example: –

public class Television implements TelevisionRemote{


   public void turnOnTelevision(){

   //method implementation details



   public  void turnOffTelevision(){

   //method implementation details



Interfaces provide contract specification or sets of rules for the classes implementing them. They set rules for classes and tell them what to do and not to do. In case the class does not provide implementation for all the methods of the interface then the class must be declared abstract. We will cover abstract classes later. They provide total abstraction which means that all the methods are empty and field variables are public static and final by default. Interfaces serve several features: –

1.They provide total abstraction.

2.They help to achieve what we call multiple inheritance as java doesn’t support multiple inheritance, but you can implement several interfaces in one class and thus it helps to achieve multiple inheritance.

3.They help to achieve loose coupling in design patterns implementation.

Abstract classes

Abstract classes are just like normal java class except they use keyword abstract in front of class declaration and method declaration.

Syntax: –

public abstract class XYZ {

public abstract methodName();


For example :

public abstract class Automobile {

   public abstract void engine();

   public void  gearBoxGearOne(){

     //method implementation



Abstract classes are created using abstract keyword and they may have or may not have method implementation. If a method is declared abstract then its implementation has to be provided by the class extending the abstract class. We can have abstract class without abstract method as well as they can contain final methods also. A class that extends the abstract class is a child class for that abstract class and has to provide implementation for the abstract method declared in the abstract class.

Example :-

public class Car extends Automobile{


   public void engine(){

     //Method implementation



Now question must be arising why we have interfaces and abstract classes. There are few key differences worth noticing : –

1.Interfaces are implicitly abstract and cannot have implementations. Abstract classes can have method implementations.

2.Variables of interfaces are final by default. Abstract classes may or may not have final variable.

3.Interface methods are public whereas abstract classes can provide all types of access modifiers for its members i.e., public, protected, private.

4.Interface can extend interface only while classes can implement multiple interfaces and can extend one class only.

Thus, both abstract classes and interfaces are used to achieve abstraction and both have their own importance while designing a java solution but most preferable choice for most developers is to use interfaces as they provide complete abstraction. I hope this article helps to clear your doubts regarding abstraction.

Java Concurrency: Thread Confinement

Thread Confinement

Most concurrency problems occur only when we want to share a mutable variable, or mutable state, between threads. If a mutable state is shared between multiple threads, then all of them will be able to read and modify the value of the state, thus resulting in incorrect or unexpected behavior. One way to avoid this problem is to simply not share the data between the threads. This technique is known as thread confinement and is one of the simplest ways of achieving thread safety in our application.

The Java language, in itself, does not have any way of enforcing thread confinement. Thread confinement is achieved by designing your program in a way that does not allow your state to be used by multiple threads and is, thus, enforced by the implementation. There are a few types of thread confinement, as described below.

Ad-Hoc Thread Confinement

Ad-hoc thread confinement describes a way of thread confinement, where it is the total responsibility of the developer, or the group of developers working on that program, to ensure that the use of the object is restricted to a single thread. This approach is very very fragile and should be avoided in most cases.

One special case that comes under Ad-hoc thread confinement applies to volatile variables. It is safe to perform read-modify-write operations on the shared volatile variable as long as you ensure that the volatile variable is only written from a single thread. In this case, you are confining the modification to a single thread to prevent race conditions, and the visibility guarantees for volatile variables ensure that other threads see the most up to date value.

Stack Confinement

Stack confinement is confining a variable, or an object, to the stack of the thread. This is much stronger than Ad-hoc thread confinement, as it is limiting the scope of the object even more, by defining the state of the variable in the stack itself. For example, consider the following piece of code:

private long numberOfPeopleNamedJohn(List<Person> people) {
  List<Person> localPeople = new ArrayList<>();
  return localPeople.stream().filter(person -> person.getFirstName().equals("John")).count();

In the above code, we pass on a list of person but do not directly use it. We, instead, create our own list, which is local to the currently executing thread, and add all the person in people to localPeople. Since we are defining our list in the  numberOfPeopleNamedJohn method only, this makes the variable  localPeople stack confined, as it exists on stack of one thread, and thus cannot be accessed by any other thread. This makes localPeople thread safe. The only thing we need to take care of here is that we should not allow localPeople to escape the scope of this method, to keep it stack confined. This should also be documented or commented when defining this variable, as generally, it’s only in the current developer’s mind to not let it escape, and in future, another developer may mess up.


ThreadLocalallows you to associate a per-thread value with a value-holding object. It allows you to store different objects for different threads and maintains which object corresponds to which thread. It has set and get accessor methods which maintain a separate copy of the value for each thread that uses it. The  get() method always returns the most updated value passed to  set() from the currently executing thread. Let’s look at an example:

public class ThreadConfinementUsingThreadLocal {
    public static void main(String[] args) {
        ThreadLocal<String> stringHolder = new ThreadLocal<>();
        Runnable runnable1 = () -> {
            stringHolder.set("Thread in runnable1");
            try {
            } catch (InterruptedException e) {
        Runnable runnable2 = () -> {
            stringHolder.set("Thread in runnable2");
            try {
                stringHolder.set("string in runnable2 changed");
            } catch (InterruptedException e) {
        Runnable runnable3 = () -> {
            stringHolder.set("Thread in runnable3");
            try {
            } catch (InterruptedException e) {
        Thread thread1 = new Thread(runnable1);
        Thread thread2 = new Thread(runnable2);
        Thread thread3 = new Thread(runnable3);

In the above example, we have executed three threads, using the same  ThreadLocal object stringHolder. As you can see here, we have, first of all, set one string in every thread in the stringHolder object, making it contain three strings. Then, after some pause, we have changed the value from just the second thread. Below was the output of the program:

string in runnable2 changed
Thread in runnable1
Thread in runnable3

As you can see in the above output, the String for thread 2 changed, but the strings for thread 1 and thread 3 were unaffected. If we do not set any value before getting the value on a specific thread from ThreadLocal, then it returns null. After a thread is terminated, thread-specific objects in ThreadLocal become ready for garbage collection.

Java Streams Overview, Part II

In my previous article, I wrote about the fundamentals of streams in Java 8. Now, let’s augment our skills with some additional information about streams, like how we can chain them, and we can use them to access files.

Chaining Streams

When working with streams, they are often chained together.

Let us see what are the advantages of using chained streams:

  • One stream instance leverages another stream instance.
  • This creates a higher level of functionality. We can have one stream accessing the data, then we have another stream that takes the results of that and processes more complex functionality.
  • This simplifies reusability because you can organize your streams in a way that allows each of them performs a specific job. In this way, they do not need to know each other’s inner workings.

We perform chaining using a constructor. We construct a higher level instance of the stream and then pass an instance of a lower level stream.

A good example of a chained stream is the InputStreamReader class, which is what we talked about in my previous article.

This class leverages chaining by providing reader behavior over an  InputStream. It translates the binary response to character behavior.

Let us see how it works.

void doChain(InputStream in) throws IOException{
int length;
char[] buffer = new char[128];
try(InputStreamReader rdr = new InputStreamReader(in)) {
while((length = rdr.read(buffer)) >= 0) {
//do something

As you can see, we do not need to care about how the  InputStream works. Whether it is backed by a file or network, it does not matter.

The only thing we know that it gives us binary data, we will pass it to our InputStreamReader and it converts it and can work with it as a character data.

Notice that we use try-with-resources here as well. If we close the InputStreamReader, it automatically closes theInputStream as well. This a very powerful concept, that you should know about.

File and Buffered Streams

We often use streams for accessing files.

There are several classes for that in the java.io package to use, like:

  • FileReader
  • FileWriter 
  • FileInputStream 
  • FileOutputStream 

The real thing is that these file streams are deprecated now. Despite that, they are still widely used in codes. So, you probably will face them in the near future, so it’s worth a notation.

Let us look at new ways to interact with files.

Buffered Streams

Buffered Streams are introduced to replace the FileStream classes in the java.io package. These new Streams are placed under the java.nio package.

It was necessary because direct file access can be inefficient and buffered streams can significantly improve efficiency with the following:

  • Buffer content in memory
  • Perform reads/writes in large chunks
  • Reduce underlying stream interaction

Buffering available for all four stream types:

  • BufferReader 
  • BufferWriter 
  • BufferedInputStream 
  • BufferedOutputStream 

Using them is very straightforward.

try(BufferedReader br = new BufferedReader(new FileReader("file.txt"))){
int value;
while((value = br.read()) >= 0) {
char charValue = (char)value;
//do something

Additional benefits to using BufferedStreams includes:

  • It handles linebreaks for various platforms like Windows or Unix
  • Uses correct value for the current platform
  • The BufferedWriter has a method:newLine(). It will create a new line with the appropriate character.
  • TheBufferedReader has a method for line based read: readLine().

Let us see how they work.


void writeData(String[] data) throws IOException {
try(BufferedWriter bw = new BufferedWriter(new FileWriter("file.txt"))){
int value;
for(String str : data) {


void readData(String[] data) throws IOException {
try(BufferedReader br = new BufferedReader(new FileReader("file.txt"))){
String inValue;
while((inValue = br.readLine()) != null) {

The code above will write out the file’s content line by line.

Accessing Files With the java.nio.file package

In Java 8, the java.io.FileXXX streams are deprecated. There is a new package to handle file streams called the java.nio.file package.

The package has several benefits over java.io:

  • Better exception reporting
  • Greater scalability, they work much better with large files
  • More file system feature support
  • Simplifies common tasks

Bellow, we will talk about the most fundamental Types In this new package.

Paths and Path Types


  • Used to locate a file system item
  • It can be a file or directory


  • Used to get the Path objects through static Path factory methods
  • It translates a string-based hierarchical path or URI to Path.

Example: Path p = Paths.get(“\\documents\\foo.txt”)

Files Type

  • Static methods for interacting with files
  • Create, copy, delete, etc…
  • Open files streams
    • newBufferedReader 
    • newBufferedWriter 
    • newInputStream 
    • newOutputStream 
  • Read/Write file contents
    • readAllLines
    • write 

Reading Lines With BufferedReader

Let us see some quick example of how you can use it.

void readData(String[] data) throws IOException {
try(BufferedReader br = Files.newBufferedReader(Paths.get("data.txt")){
String inValue;
while((inValue = br.readLine()) != null) {

Read All lines

void readAllLines(String[] data) throws IOException {
List<String> lines = Files.readAllLines(Paths.get("data.txt"));
for(String line:lines) {

File Systems

When we work with files from a Java program, those files are contained within a file system. Most commonly, we use the computer’s default file system.

Java also supports specialized file systems, such as the Zip file system.

Path instances are tied to a file system and thePath class works only for the default one. So, we need another solution. Fortunately, in the Java.nio package, we have the opportunity to deal with this.

File System Types


  • Represents an individual file system
  • Factory for Path instances


  • Used to get the FileSystem objects through static FileSystem factory methods
  • Open or create a file system
    •  newFileSystem

Accessing File Systems

File systems identified by URIs

  • Specifics of URI vary greatly among the file systems
  • Zip file system uses “jar:file” scheme
    • jar:file:/documents/data.zip

File systems support custom properties

  • Different for each file system type
  • Examples: String encoding, whether to create if it does not exist

Creating a Zip Filesystem

public static void main(String[] args) throws FileNotFoundException, IOException {
try (FileSystem zipFileSystem = openZip(Paths.get("data.zip"))){ //pass the Path where we would like to create our FileSystem
}catch (Exception e) {
System.out.println(e.getClass().getSimpleName() + " - " + e.getLocalizedMessage());;
private static FileSystem openZip(Path path) throws URISyntaxException, IOException {
Map<String, String> properties = new HashMap<>();
properties.put("create", "true"); //set the property to allow creating
URI zipUri = new URI("jar:file", path.toUri().getPath(), null); //make a new URI from the path
FileSystem zipFileSystem = FileSystems.newFileSystem(zipUri, properties); //create the filesystem
return zipFileSystem;

After the code above, you should see your data.zip file in your directory.

Copying Files to Zip Filesystem

Let us augment the above example with a File copy operation.

In this example, I created a file called file.txt in my project library. We will copy this file to our data.zip Filesystem.

Streams in Java 8
public static void main(String[] args) throws FileNotFoundException, IOException {
try (FileSystem zipFileSystem = openZip(Paths.get("data.zip"))){
copyFileToZip(zipFileSystem); //Here we call the file copy
}catch (Exception e) {
System.out.println(e.getClass().getSimpleName() + " - " + e.getLocalizedMessage());;
private static FileSystem openZip(Path path) throws URISyntaxException, IOException {
Map<String, String> properties = new HashMap<>();
properties.put("create", "true");
URI zipUri = new URI("jar:file", path.toUri().getPath(), null);
FileSystem zipFileSystem = FileSystems.newFileSystem(zipUri, properties);
return zipFileSystem;
static void copyFileToZip(FileSystem zipFileSystem) throws IOException{
Path sourceFile = FileSystems.getDefault().getPath("file.txt"); //Read the file to copy
Path destFile = zipFileSystem.getPath("/fileCopied.txt"); //get the path of the new file
Files.copy(sourceFile, destFile);//Copy the file to our zip FileSystem

After you run the code, you should see the fileCopied.txt int our zip-file. Its context should be the same as in our file.txt.


In this article, we went further into streams in Java 8. I demonstrated how stream chaining works, as well as how you can deal with files through the new java.nio package. We also touched on why you should use more up-to-date, buffered versions of the Filestreams.

Hope you enjoyed!

How Much Memory Does a Java Thread Take?

A memory, which is taken by all Java threads, is a significant part of the total memory consumption of your application. There are a few techniques on how to limit the number of created threads, depending on whether your application is CPU-bound or IO-bound. If your application is rather IO-bound, you will very likely need to create a thread pool with a significant number of threads which can be bound to some IO operations (in blocked/waiting state, reading from DB, sending HTTP request).

However, if your app rather spends time on some computing task, you can, for instance, use HTTP server (e.g. Netty) with a lower number of threads and save a lot of memory. Let’s look at an example of how much memory we need to sacrifice to create a new thread.

Thread memory contains stack frames, local variables, method parameters, … and a thread size can is configured with defaults this way (in kilobytes):

$ java -XX:+PrintFlagsFinal -version | grep ThreadStackSize 
intx CompilerThreadStackSize    = 1024  {pd product} {default}
intx ThreadStackSize            = 1024  {pd product} {default}
intx VMThreadStackSize          = 1024  {pd product} {default}

Thread Memory Consumption on Java 8

$ java -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary /
-XX:+PrintNMTStatistics -version
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_212-b03)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.212-b03, mixed mode)
Native Memory Tracking:
Total: reserved=6621614KB, committed=545166KB
- Java Heap (reserved=5079040KB, committed=317440KB)
  (mmap: reserved=5079040KB, committed=317440KB) 
-  Class (reserved=1066074KB, committed=13786KB)
    (classes #345)
    (malloc=9306KB #126) 
    (mmap: reserved=1056768KB, committed=4480KB) 
-  Thread (reserved=19553KB, committed=19553KB)
   (thread #19)
    (stack: reserved=19472KB, committed=19472KB)
    (malloc=59KB #105) 
    (arena=22KB #34)

We can see two types of memory:

  • Reserved — the size which is guaranteed to be available by a host’s OS (but still not allocated and cannot be accessed by JVM) — it’s just a promise
  • Committed — already taken, accessible, and allocated by JVM

In a section Thread, we can spot the same number in Reserved and Committed memory, which is very close to a number of threads * 1MB. The reason is that JVM aggressively allocates the maximum available memory for threads from the very beginning.

Thread Memory Consumption on Java 11

$ java -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary /-XX:+PrintNMTStatistics -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.2+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.2+9, mixed mode)
Native Memory Tracking:Total: reserved=6643041KB, committed=397465KB
-   Java Heap (reserved=5079040KB, committed=317440KB)
(mmap: reserved=5079040KB, committed=317440KB) 
-   Class (reserved=1056864KB, committed=4576KB)
(classes #426)(  instance classes #364, array classes #62)
(malloc=96KB #455) 
(mmap: reserved=1056768KB, committed=4480KB) 
(  Metadata:   )
(    reserved=8192KB, committed=4096KB)
(    used=2849KB)
(    free=1247KB)
(    waste=0KB =0,00%)
(  Class space:)
(    reserved=1048576KB, committed=384KB)
(    used=270K
(    free=114KB)
(    waste=0KB =0,00%)
-  Thread (reserved=15461KB, committed=613KB)
(thread #15)
(stack: reserved=15392KB, committed=544KB
(malloc=52KB #84) 
(arena=18KB #28)

You may notice that we are saving a lot of memory just because we are using Java 11, which no longer aggressively allocates up to Reserved Memory at the time of thread creation. Of course, this is just java -version command, but if you try it out, you will definitely notice a big improvement.


Java Streams Overview

For a long time, I had a gap in my knowledge of Java streams. On a basic level, I could use them, but I did not a deep understanding of them. So, I decided to do a quick overview of Java streams.

In this article, I will deal with many everything from the fundamentals to chaining and clean-up.

Hope this helps you broaden your knowledge of Java streams.

What Are Streams?

A stream is an ordered sequence of data that…

  • Provides a common I/O model
  • Abstracts details from an underlying source or destination

Whether you use streams to take data from memory, storage, or your network, it will hide the implementation details from you. The details are abstracted away, so in every situation, you can look at it as an ordered sequence of data.

  • Stream types are uni-directional

This means that if you create an instance of a Java stream, you decide whether you would like to write to it or read from it. You can’t do both at the given time on a single stream.

Read / Write

We can divide the streams into two categories:

  • Byte streams – Interacts as binary data
  • Text streams – Interacts as unicode characters

The general interaction is the same for both Java stream types

Reading With Streams

As we mentioned, each stream is used either to read from or write to.

Firstly, let us see how we can read data from Java streams.

Java streams

The base class to read binary data in Java is the InputStreamAnd the base class to read text data is called the Readerclass.

Both classes almost have the same two methods:

  •  int read()
  •  int read(byte/char[] buff)

Notice that in both scenarios, they give back an Integer value. These are interpreted values. An integer is a 32-bit container, so it will work in both cases.

The difference between the two:

  • The InputStream works with bytes, which are 8-bits.
  • The Reader works with unicode characters, which are 16-bits.

Read Bytes With InputStream

InputStream input = // create input stream
int result;
while(result = input.read() >= 0) //Indicates the end-of-stream with a return value of -1
byte byteVal = (byte)result;
// do something with byteVal

Read Text with Reader

Reader reader = // create reader
int result;
while(result = reader.read() >= 0)//Indicates the end-of-stream with a return value of -1
char charVal = (char)result;
// do something with charVal

Note that if you would like to retrieve the value, you simply need to cast the result to the appropriate type — in this case, byte or char.

Writing With Streams

To write data, there are two base classes similar to the read streams.

  •  OutputStream (for bytes)
  •  Writer (for text)
Java Text streams
To write with Java streams is more straightforward than reading them. Both classes have a few   write  methods with the  void return type.

Writing Bytes With OutputStream

To write with OutputStream, you can pass a single byte the write method, or you can pass a byte array as well.

OutputStream output // create output stream;
Byte byteVal = 100;
byte[] byteBuff = {0, 10, 20};

Writing Characters With Writer

To write with the Writer class, you can pass a simple character, character array, or String to its  write method.

Writer writer // create output stream;
char charVal = 'c';
char[] charArray = {'c', 'h', 'a', 'r'};
String stringVal = "String";

As you can see, you need much less code to write than to read.

Common Java Stream Classes

Above, I wrote about the base stream classes. Now, let us go a bit deeper and talk about the different implementations.

Common Input/OutputStream Derived Classes

Java streams
  •  ByteArrayInputStream /  ByteArrayOutputStream – Enables us to create a stream over a byte array
  •  PipedInputStream PipedOutputStream – This is much like a producer-consumer concept. A piped output stream can be connected to a piped input stream to create a communications pipe. The piped output stream is the sending end of the pipe. Typically, data is written to a PipedOutputStream object by one thread and data is read from the connected PipedInputStream by some other thread.
  •  FileInputStream /  FileOutputStream – These allow us to create streams over files.

Common Reader/Writer Derived Classes

Java Reader/ Writer streams
Above are examples of Reader/Writer stream implementations.
  •  CharArrayReader / Writer – Allows creating streams over characters
  •  StringReader / Writer – Allows creating stream over Strings
  •  PipedReader / Writer – Allows creating a stream in a Producer/Consumer relationship over text. Similarly to the PipedOutput InputStream 
  •  InputStreamReader / Writer – Allows us to create a stream over an Input OutputStream 
  •  FileReader / Writer – These are delivered from the least mentioned above. It allows us to make a stream over text files.

Stream Errors and Clean-Up

So far, we looked at the general features of streams, but we have not considered all the realities of working with them.

Stream Realities

Java streams

Let us see the two main groups here.



  • Streams are backed by physical storage, which often exists outside the Java runtime, like files or network connections.
  • Hence, Java may not reliably clean up, so we need to do our own reliable clean-up. We need to close the Streams when we are done with them.


  • Streams implement the Closeable interface, which implements one single close method. So, this is our responsibility to call it.

Let us see a simple solution:

Reader reader;
reader = // create output stream;
// do something with reader;
}catch (IOException e) {
//handle exception
}finally {
if(reader != null)

The problem with the above example is that you always need to implement it. Usually, we use Streams frequently, so it should be done automatically. Let us see how we can achieve it.

Automating Clean-Up

  •  AutoClosable interface
    • One method: close
    • The base interface of the Closable interface, so every Stream supports it.
    • Provides support for try-with-resources


  • Automates the clean-up of one or more resources
    • A “resource” is any type that implements  AutoClosable
  • Syntax similar to traditional try statement
  • Optionally includes catch block(s)
    • Handle try body
    • Handle close method call

Working With Try-With-Resources

Here, I provided a simple example of how we can use the automatic close of streams with try-with-resources block.

I will use it through a FileInputStream. We will talk about this specific stream later.

 try(FileInputStream input = new FileInputStream("file1.txt")) {
        int data = input.read();
        while(data != -1){
            System.out.print((char) data);
            data = input.read();

With the above approach, you do not need to investigate further work to close your streams.


In this article, we talked about the fundamentals of Java streams, what are they, how they work, and how you can use them. In my next article, I will dig a bit deeper, and I will write about more advanced Stream topics like chaining, buffered streams, and how to use file systems with it. So stay tuned!

See more about streams in the official Java documentation here.

Final Keyword and JVM Memory Impact

For a majority of developers, the keyword final is a well-known concept when we talk about composing a Java class. In general, it forbids the overriding of classes and methods, as well as changes a variable that has been already initialized. However, this is only one part (and I think the smaller part) of the final keyword definition. The other part is the connection to JVM memory, and that’s actually what this post is about!

I ‘d like to discuss this topic further by providing some examples, but if you want to really dive deeper, then I recommend Chapter 17 from Java Language Specification. Let’s dive right in!

Why Should I Bother Adding the Final Keyword?

I know that I’m not going to change the initialized value in my very small class, so why should care about this keyword; isn’t it just blowing up my concise Java source file?

NO! There is other cool stuff from the final keyword.

  • Guarantees visibility in a multi-threaded application
  • Safe initialization for objects, arrays, and collections

And please, don’t argue that you don’t use threads in your application — it is very likely that you use a framework that actually does, just under the hood.

How I Actually Reason With Object Initialization

Let’s step back a bit from the final keyword and demonstrate what object initialization might look like. It helps us understand where racing between threads may appear.

Image title

The picture above shows a small Java class and a description of how the initialization of that class could be done in reality. There is no concept of classes in a native language, which means the program needs to be expressed by instructions, one after another.

In this case, we can see that the object itself was created in the first line, and then, we populated the fields and published the reference to make it available and accessible. Everything looks fine, but there are several important considerations:

  • To make your program faster, the JVM and CPU can reorder the instructions to delay store instruction on some variable as late as possible. A very simple explanation would be: JVM and CPU can do everything with your code until the PROGRAM ORDER is satisfied. This means that the JVM can move store and read instructions but the behavior of your program must remain unchanged (we can go deeper into that in a different blog post).

So far, so good, but why am I supposed to be scared when the behavior of my program cannot be changed?

  • PROGRAM ORDER is followed only in a single thread. This means that if you run your application in a multi-threaded environment, then you have to inform the JVM that there is a possibility to complete a race condition when the JVM starts optimizing your code.
  • In a nutshell, it means that your second thread can see a partially initialized object, let’s say, because a step “publish reference (the last step from the picture) can be reordered with the field assignment “temp.y,” and then, the second thread will see a reference to the object with null value instead of the value “2.”

Let’s Introduce the Term: Safe Publication

The question is: How can we publish the shared object (a global variable) and be sure that all threads in our program will see the properly initialized object?

  • Initialize an object reference from a static initializer
    • This is a typical singleton holder pattern
    • The reason why this is safe is that because the JVM always performs object initialization on a single thread and ensures that the initialization happens before everything that uses that class (no other synchronization needed)
  • Use volatile or java.util.concurrent.atomic.Reference
  • Use locks to guard the global variable
  • Or … use the final field and never leak THIS from the constructor of the given class!

How Specification Ensures Safe Publication Using a Final Field

We already mentioned that the JVM or CPU is able to reorder everything and make your program faster. This means that we have to have some mechanism to inform the entire environment — please, don’t optimize this part.

  • JVM adds a synthetic freeze action (yes, this is an official name) before a constructor completes
    • Freeze action is added at runtime, and there is no bytecode instruction for this
  • Freeze ensures that the second thread sees null or an object reference with a fully constructed field, which was marked as final
  • On a CPU level, the freeze is implemented as a store barrier (sfence instruction on x86)
  • Other operations after the freeze are racing between two threads and must be properly synchronized

The JVM ensures that the second thread sees the state of the final field corresponding to the freeze action — no more, no less.

Image title

First, let’s agree on how I described steps of the execution:

  • Variabletemp is the given object that we want to publish.
  • Using  nfv = temp, we expose the publication of a reference of our object.
  •  <freeze value> means the point of the execution where the JVM inserts the freeze action.

On the picture above, we can see a very simple example of what can go wrong. We’ve already mentioned exactly this case which fails because of reordering the linetemp.value = 1 after the object publication nfv = temp.

On the contrary, the example on the right side shows fully and properly exposed our object to all threads. Freeze ensures that there is no reordering, which means that the publication of our object can be executed only when our field is properly pushed to a memory where it can be shared with other threads (Main Memory, or shared via Cache Coherence).

Image title

These examples show that the final field can help us correctly expose, even collections and arrays:

  • Collections and arrays are properly exposed, even with all the values inside.
  • The values inside the collection are properly exposed even if they don’t have final fields
  • BUT if we add a new value after the construction of our object, then this item is not supposed to be exposed properly, and Thread 2 can see the collection in both possible states.

Image title

The  Composition class shows a simple fact: Even if an object with non-final fields is exposed using a final field, then the inner object itself becomes properly exposed.

Image title

This example is very tricky. Don’t count on the behavior of an implementation, always code against the specification!

OpenJDK Hotspot behaves in a way that even if only one field is marked as final, it ensures that the freeze action is added “at the end of our constructor,” and flushing the CPU write buffers ensures that all other fields become visible to other threads. It works; you can check out other examples on my GitHub (link below) with JStress and give it a shot.
Image title

ConcurrentHashMap ensures that all values added to our Map are visible immediately to threads (internally synchronized).

However, it does not mean that the field holding the map, which is not marked as final, is visible to other threads after ConcurrentUtil construction. In this case, we can see the object with null value instead of initialized  ConcurrentHashMap.

What If I Cannot Use Final Fields…

We sometimes reach the situation where we are not able to modify a class and change all of its fields to final. So, is there any other way to expose the class properly with all the non-final fields?

We can use approaches that ensure we get safe publication for free:

  • Static Initializer (Singleton Holder pattern)
  • Thread-confinement
    • Run a block of code (task, job, HTTP request, …) only on one dedicated thread
    • Frameworks with a matured and well-document thread model, e.g. Event Loops
  • Stack-confinement
    • An object is reachable only through local variables
  • ThreadLocal
    • Maintains a per-thread value

Or, we can use some synchronization tools:

  • Synchronization block (implicit lock)
  • Explicit locks, volatile

The java.util.concurrent package, which does not ensure that a non-final field itself will be properly visible, contains objects that are used by those classes and makes sure they are properly exposed and visible to other threads. The classes below passes the object from one thread to another via some internal memory barrier (e.g. unsafe, or volatile field) that means that the object, which is created and written to any structure below by Thread A and then is read by Thread B, is fully constructed even if the object contains non-final fields.

Image title

  • CAS Operations
    •  Atomic*,  *AdderAtomic*FieldUpdater
  • java.util.concurrent package
    •  SynchronizedMapConcurrentHashMap
    •  CopyOnWriteArray(List|Set) 
    •  Synchronized(List|Set) 
    •  BlockingQueueConcurrentLinkedQueue
  •  VarHandlesUnsafe?

Thank you for reading my article and please leave comments below. If you would like to be notified about new posts, then start following me on Twitter: @p_bouda.

Java 8 Functional Interfaces – When & How To Use Them?

Functional interfaces, lambda expressions and Stream API – these three features of Java 8 has turned Java programming into new style of programming called functional-style programming. Java is still an object-oriented programming language, but from Java 8, with the introduction of new features, most of the programming is done keeping functions in mind rather than objects. In this article, we will see Java 8 functional interfaces, @FunctionalInterface annotation, java.util.function package and how to use new Java 8 functional interfaces to compose lambda expressions with some simple examples.

Java 8 Functional Interfaces

1) Definition

Functional interfaces are the interfaces which has exactly one abstract method. They may have any number of default methods but must have only one abstract method. Functional interfaces provide only one functionality to implement.

There were functional interfaces exist before Java 8. It is not like that they are the whole new concept introduced only in Java 8. RunnableActionListenerCallable and Comaprator are some old functional interfaces which exist even before Java 8.

The new set of functional interfaces are introduced in Java 8 to make programmer’s job easy while writing lambda expressions. Your lambda expression must implement any one of these functional interfaces. These new functional interfaces are organised under java.util.function package.

2) @FunctionalInterface Annotation

@FunctionalInterface annotation is introduced in Java 8 to represent functional interfaces. Although, it is not compulsory to write functional interface using this annotation. But, if you are using @FunctionalInterface annotation then your interface should contain only one abstract method. If you try to write more than one abstract method, compiler will show the error.

3) java.util.function package

All Java 8 functional interfaces are organised in java.util.function package. Each functional interface in this package represents an operation that can be performed by the lambda expression.

Below table shows the list of all Java 8 functional interfaces along with their abstract method, which operation they represent and when to use them?

Java 8 functional interfaces

4) How To Use Java 8 Functional Interfaces In Real Time?

Let’s define Student class like below. We will be using this class in the subsequent examples.

class Student
    int id;
    String name;
    double percentage;
    String specialization;
    public Student(int id, String name, double percentage, String specialization)
        this.id = id;
        this.name = name;
        this.percentage = percentage;
        this.specialization = specialization;
    public int getId() {
        return id;
    public String getName() {
        return name;
    public double getPercentage() {
        return percentage;
    public String getSpecialization() {
        return specialization;
    public String toString()
        return id+"-"+name+"-"+percentage+"-"+specialization;

Let listOfStudents be the list of 10 students.

List<Student> listOfStudents = new ArrayList<Student>();
listOfStudents.add(new Student(111, "John", 81.0, "Mathematics"));
listOfStudents.add(new Student(222, "Harsha", 79.5, "History"));
listOfStudents.add(new Student(333, "Ruth", 87.2, "Computers"));
listOfStudents.add(new Student(444, "Aroma", 63.2, "Mathematics"));
listOfStudents.add(new Student(555, "Zade", 83.5, "Computers"));
listOfStudents.add(new Student(666, "Xing", 58.5, "Geography"));
listOfStudents.add(new Student(777, "Richards", 72.6, "Banking"));
listOfStudents.add(new Student(888, "Sunil", 86.7, "History"));
listOfStudents.add(new Student(999, "Jordan", 58.6, "Finance"));
listOfStudents.add(new Student(101010, "Chris", 89.8, "Computers"));

Let’s see how to use 4 important functional interfaces – PredicateConsumerFunction and Supplier using above listOfStudents.

a) Predicate – Tests an object

Predicate represents an operation which takes an argument T and returns a boolean. Use this functional interface, if you want to define a lambda expression which performs some test on an argument and returns true or false depending upon outcome of the test.

For example,

Imagine an operation where you want only a list of “Mathematics” students from the above listOfStudents. Let’s see how to do it using Predicate.

Lambda expression implementing Predicate : Checking specialization of a Student

Predicate<Student> mathematicsPredicate = (Student student) -> student.getSpecialization().equals("Mathematics");
List<Student> mathematicsStudents = new ArrayList<Student>();
for (Student student : listOfStudents)
    if (mathematicsPredicate.test(student))

b) Consumer – Consumes an object

Consumer represents an operation which takes an argument and returns nothing. Use this functional interface If you want to compose a lambda expression which performs some operations on an object.

For example, displaying all students with their percentage.

Lambda expression implementing Consumer : Displaying all students with their percentage

Consumer<Student> percentageConsumer = (Student student) -> {
        System.out.println(student.getName()+" : "+student.getPercentage());
for (Student student : listOfStudents)

c) Function – Applies to an object

Function represents an operation which takes an argument of type T and returns a result of type R. Use this functional interface if you want to extract some data from an existing data.

For example, extracting only the names from listOfStudents.

Lambda expression implementing Function : Extracting only the names of all students

Function<Student, String> nameFunction = (Student Student) -> Student.getName();
List<String> studentNames = new ArrayList<String>();
for (Student student : listOfStudents)

d) Supplier – Supplies the objects

Supplier represents an operation which takes no argument and returns the results of type R. Use this functional interface when you want to create new objects.

Lambda expression implementing Supplier : Creating a new Student

Supplier<Student> studentSupplier = () -> new Student(111111, "New Student", 92.9, "Java 8");

5) Functional Interfaces Supporting Primitive Type

Java 8 has also introduced functional interfaces which support primitive types. For example IntPredicateDoublePredicateLongConsumer etc… (See above table).

If an input or output is a primitive type then using these functional interfaces will enhance the performance of your code. For example, if input to a Predicate is primitive type intthen using intPredicate instead of Predicate will remove unnecessary boxing of input.