Java Tutorial

Java Control Statements

Object Oriented Programming

Java Built-in Classes

Java File Handling

Java Error & Exceptions

Java Multithreading

Java Synchronization

Java Networking

Java Collections

Java Interfaces

Java Data Structures

Java Collections Algorithms

Advanced Java

Java Miscellaneous

Java APIs & Frameworks

Java Class References

Java Useful Resources

Java 8 - New Features



JAVA 8 is a major feature release of JAVA programming language development. Its initial version was released on 18 March 2014. With the Java 8 release, Java provided supports for functional programming, new JavaScript engine, new APIs for date time manipulation, new streaming API, etc.

Following is the list of new features supported in Java 8:

Lambda Expressions

Lambda expression is one of the biggest feature introduced in Java. A lambda expression facilitates functional programming in Java. A lambda expression works on the principle of functional interface. A Functional interface is an interface with only one method to implement. A lambda expression provides an implementation of the functional interface method.

Lambda expression simplifies functional programming a lot and makes code readable without any boilerplate code. A lambda expression can infer the type of parameter used and can return a value without a return keyword. In the case of the simple one-statement method, even curly braces can be eliminated.

Example - Using Lambda Expressions

Following example showcases the use of lambda expression. A lambda expression works best with a functional interface, an interface with single abstract method. We've defined one interface Calculator with single method operate, which can accept two parameters and return a value. In main method, we've implemented the Calculator interface using anonymous function first and then using a lambda expression. The operate() method is called to print the result in both cases and results are printed.

package com.tutorialspoint;

public class Tester {

   public static void main(String[] args) {
      // Interface implementation using anonymous class
      Calculator sum = new Calculator() {
         @Override
         public int operate(int a, int b) {
            return a + b;
         }
      };
      int result = sum.operate(2,3);
      System.out.println(result);	   

      // Interface implementation using lambda expression
      Calculator sum1 = (a,b) -> a + b;
      result = sum1.operate(2,3);
      System.out.println(result);
   }  

   interface Calculator {
      int operate(int a, int b);
   }
}

Let us compile and run the above program, this will produce the following result −

5
5

Method References

Method reference is a short and concise way to call methods, static methods and even constructors without any lengthy syntax. Method references help to point to methods by their names even without specifying the arguments. Arguments are passed by the lambda expression. A method reference is described using "::" symbol.

A static method can be referred using following syntax:

<<class-name>>::methodName

An instance method can be referred using following syntax:

<<object-name>>::methodName

We can invoke constructor using following syntax:

<<class-name>>::new

Example - Using Method References

In this example, we've used a static method compare and an instance method compareTo to sort two arraylist of integers. We've used method references to represent both static and instance methods.

package com.tutorialspoint;

import java.util.Arrays;
import java.util.List;

public class Tester {
   public static void main(String args[]) {
      List<Integer> numbers = Arrays.asList(1,2,4,9,8,7,3);
      System.out.println("Sorted using static method reference");
      // Use static method compare
      numbers = numbers.stream().sorted(Integer::compare).toList();
      System.out.println(numbers);

      numbers = Arrays.asList(1,2,4,9,8,7,3);
      System.out.println("Sorted using instance method reference" );
      // Use instance method compareTo
      numbers = numbers.stream().sorted(Integer::compareTo).toList();

      System.out.println(numbers);		
   }
}

Let us compile and run the above program, this will produce the following result −

Sorted using static method reference
[1, 2, 3, 4, 7, 8, 9]
Sorted using instance method reference
[1, 2, 3, 4, 7, 8, 9]

Default Methods

Before Java 8, an interface could have only abstract methods. With Java 8, lambda expression were introduced. Now for backward compatability, default method capability was added so that old interfaces can leverage lambda expression without modifying their implementations.

For example, List or Collection interfaces do not have 'forEach' method declaration. Thus, adding such method will simply break the collection framework implementations. Java 8 introduces default method so that List/Collection interface can have a default implementation of forEach method, and the class implementing these interfaces need not implement the same.

Syntax

The following is the syntax of the default method in interface in Java −

public interface vehicle {
   default void message() {
      System.out.println("I am a vehicle!");
   }
}

Example - Using Default Method

In this example, we've created an interface with a default method. In an implementing class, this message is not implemented and is used to print a message.

package com.tutorialspoint;

interface vehicle {
   // default method must have an implementation
   default void message() {
      System.out.println("I am a vehicle!");
   }
}

// implementing class need not to implement the default method
// of an interface.
public class Tester implements vehicle {
   public static void main(String args[]) {
      Tester tester = new Tester();
      // implementing class can access the default method as its own method
      tester.message(); 
   }
}

Let us compile and run the above program, this will produce the following result −

I am a vehicle!

Stream API

Stream API is a new abstract layer introduced in Java 8 to process data in a declarative way. A stream represents a sequence of elements. A stream provides a set of elements of specific type in a sequential manner. A stream gets/computes elements on demand. It never stores the elements.

Stream supports aggregate operations like filter, map, limit, reduce, find, match, and so on and can do the iterations internally over the source elements provided, in contrast to Collections where explicit iteration is required.

Syntax

Following is the generic syntax to use a stream

<<collection-instance>>.stream().<<non-terminal-operation()>>.<<non-terminal-operation()>>.<<terminal-operation()>>

Example - Using Stream

In this example, we've created a list of strings where few entries are empty. Now using stream API, we're filtering the empty strings and counting them.

package com.tutorialspoint;

import java.util.Arrays;
import java.util.List;

public class Tester {
   public static void main(String args[]) {
      List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

      // get stream from list using stream() method
      // then apply filter
      // lastly count the result of filter
      long count = strings.stream().filter(string->string.isEmpty()).count();
      System.out.println("Empty Strings: " + count);
   }
}

Let us compile and run the above program, this will produce the following result −

Empty Strings: 2

Optional Class

Optional class feature was introduced in java 8 to handle Null Pointer Exception scenarios programmatically, to make programs more concise, less error prone. A Null Pointer Exception occurs whenever a null object reference is used to get value from it or to invoke its method. As program size increases, it is very tedious to handle all cases where Null Pointer Exception can happens.

Optional Class instance provides a wrapper over the object with many utility methods like to get the alternate value if underlying value is null, to check if object reference is null and so.

Example - Using Optional Class

In this example, we've created two Optional class instance using oofNullable() method which allows to pass underlying object as null and then retrieved the value using orElse() method, which returns a default value if underlying object is null.

package com.tutorialspoint;

import java.util.Optional;

public class Tester {
   public static void main(String args[]) {
      // case 1: Optional is having null as underlying value
      Optional<Integer> valueOptional = Optional.ofNullable(null);

      // case 2:  Optional is having not null as underlying value
      Optional<Integer> valueOptional1 = Optional.ofNullable(Integer.valueOf(10));

      // orElse will return -1 being default value
      Integer value = valueOptional.orElse(Integer.valueOf(-1));

      System.out.println(value);

      //  orElse will return the underlying value
      Integer value1 = valueOptional1.orElse(Integer.valueOf(-1));

      System.out.println(value1);
   }
}

Let us compile and run the above program, this will produce the following result −

-1
10

New Date Time API

Java 8 introduced a new Date Time API which is thread safe, zone ready and multiple direct methods to handle date operations. Earlier date-time API was not thread safe and in concurrency issues could pop up while working with dates. New date time APIs are using immutable constructs and no setter method thus making API more secure. The new API is designed considering zone, domain specific requirements.

Java 8 introduces a new date-time API under the package java.time. Following are some of the important classes introduced in java.time package.

  • Local − Simplified date-time API with no complexity of timezone handling.

  • Zoned − Specialized date-time API to deal with various timezones.

Example - Using Date Time APIs

In this example, we've created two Optional class instance using oofNullable() method which allows to pass underlying object as null and then retrieved the value using orElse() method, which returns a default value if underlying object is null.

package com.tutorialspoint;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZonedDateTime;

public class Tester {
   public static void main(String args[]) {
      // Get the current date and time
      LocalDateTime currentTime = LocalDateTime.now();
      System.out.println("Current DateTime: " + currentTime);

      LocalDate date1 = currentTime.toLocalDate();
      System.out.println("date1: " + date1);

      Month month = currentTime.getMonth();
      int day = currentTime.getDayOfMonth();
      int seconds = currentTime.getSecond();

      System.out.println("Month: " + month +", day: " + day +", seconds: " + seconds);

      ZonedDateTime date2 = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]");
      System.out.println("date2: " + date2);
   }
}

Let us compile and run the above program, this will produce the following result −

Current DateTime: 2024-03-07T10:29:15.650806
date1: 2024-03-07
Month: MARCH, day: 7, seconds: 15
date2: 2007-12-03T09:45:30+05:00[Asia/Karachi]

Nashorn JavaScript Engine

Nashorn, a very powerful and efficient Javascript engine, is introduced as a replacement of existing javascript engine, Rhino. Nashorn engine is touted to be 2 to 10 times faster as it can directly compile the JavaScript Code to bytecode. Nashorn engine allows to run execute JavaScript code in Java file and we can even execute java code within a JavaScript code snippet. With Nashorn engine, a command line tool jjs was introduced to run the javascript in command line tools.

Execute JavaScript Directly in Command Prompt

Open the console and type jjs and press enter button. jjs tool will open an interactive session. Once jjs session is open, we can execute a javascript code. Once done, type quit() and press enter button to exit the jjs interactive session and to return back to the command prompt.

Example

C:\JAVA>jjs
jjs> print("Hello, World!")
Hello, World!
jjs> quit()
>>
C:\JAVA>

Example - Using Javascript code within Java Code

Java has a ScriptEngineManager class since Java 6, which is used in this example to load the javascript engine as ScriptEngine instance. Once engine is loaded in the java code, we can call eval() method to evaluate a JavaScript code in Java. We can even use Java variable(s) in javascript code snippet.

package com.tutorialspoint;

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;

public class Tester {

   public static void main(String args[]) {
      // create the script engine manager   
      ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
      // load the Nashorn javascript engine
      ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");
		
      String message = "This is a message";
      String expression = "10 + 2";
      Integer result = null;
      
      try {
         // call the javascript function, pass a java variable	  
         nashorn.eval("print('" + message + "')");
         // call the javascript function and get the result back in java
         result = (Integer) nashorn.eval(expression);
         
      } catch(ScriptException e) {
         System.out.println("Error executing script: "+ e.getMessage());
      }
      System.out.println(result.toString());
   }
}

Let us compile and run the above program, this will produce the following result −

This is a message
12

Nashorn engine was deprecated in java 11 and removed in java 15 and is replaced by GraalVM javascript engine.

Advertisements