Features of Java 8

Introduction

This article is a light introduction to the Java Platform, Standard Edition 8 (Java SE 8 ) and JDK 8. After Java 5 (released in 2004), Java brings hundreds of new features to Java as a language, its compiler, tools, libraries and the JVM itself. After reading this article, you should have a basic knowledge of the new features. If you aren't familiar with the Java language, it might be hard to understand some points.

1) Lambda Expressions

Lambda Expressions is one of the biggest new feature at the language level support in Java 8.  Simply it is a method without declaration, like access modifier, declaration, return value and name. It enable us to treat functionality as a method argument, or code as data. In other words it allows us to write a method in the same place where we will use it. A Lambda Expression is useful when a method is being used only once. The syntax to create a Lambda Expression is:

Syntax: arguments -> body.

Example: myString -> System.out.println(myString).

It contains parentheses, an arrow token and a body (single expression or statement of block). It has an input argument, a body and an optional return value like a normal Java method.

  1. import java.io.*;  
  2. import java.util.*;  
  3. import java.util.function.Predicate;  
  4. public class lambda_demo   
  5. {  
  6.     public static void main(String [] a)    
  7.     {  
  8.         List<Integer> list = Arrays.asList(12345);  
  9.         System.out.println("List Contain");  
  10.         evaluate(list, (n)->true);  
  11.         System.out.println("Even Numbers are:");  
  12.         evaluate(list, (n)-> n%2 == 0 );  
  13.         System.out.println("Odd Numbers are:");  
  14.         evaluate(list, (n)-> n%2 == 1 );  
  15.         System.out.println("Numbers smaller than 3 are:");  
  16.         evaluate(list, (n)-> n < 5 );  
  17.         System.out.println("Numbers greater than 3 are:");  
  18.         evaluate(list, (n)-> n > 5 );  
  19.      }  
  20.      public static void evaluate(List<Integer> list, Predicate<Integer> predicate)
  21.      {  
  22.         for(Integer n: list)    
  23.         {  
  24.             if(predicate.test(n))   
  25.             {  
  26.                 System.out.println(n + " ");  
  27.             }  
  28.         }  
  29.     }  
  30. }  

Output

 

2) Method Reference

The other important feature in Java 8 is Method Reference that provides a way to use a method without executing it. Simply if we already have a method name, we can easily read something.

 There are the following four types of Method Reference:

  • Reference to a static method.
  • Reference to an instance method of a specific object.
  • Reference to an instance method of an arbitrary object of a specific type.
  • Reference to a constructor.
Let's take an example: 
  1. import java.util.function.Supplier;  
  2. public class MRDemo  
  3. {  
  4.    public static void main(String[] args)  
  5.    {  
  6.       String s = "Example of method references ";  
  7.       print(s::toString);  
  8.       print(() -> s.toString());  
  9.       print(new Supplier<String>()  
  10.       {  
  11.          public String get()  
  12.          {  
  13.             return s.toString();   
  14.          }  
  15.       });  
  16.    }  
  17.    public static void print(Supplier<String> supplier)  
  18.    {  
  19.       System.out.println(supplier.get());  
  20.    }  
  21. }  
Output


3) Streams

It is located in the "java.util.stream" pakage. This feature is added to the Java Collections API. In this the process of collecting an object is done in a different way. Because it does not store data, a Collection or Array is the source of the data to the stream. It is like an iterator that allows a single run over the collection. It supports the map, filter, reduce pattern and executes lazily. It can run sequentially or in parallel depending on requirements.

We can do a Core stream operation using the following methods:

  • filter(),
  • map(),
  • sorted(),
  • forEach(),
  • collect(),
  • match(),
  • count(),
  • reduce().
Some of the examples of implementing it

Using "Stream.of":

  1. import java.util.stream.*;  
  2. public class sd1 
  3. {  
  4.      public static void main(String[] args)
  5.      {  
  6.          Stream<Integer> stream = Stream.of(1,2,3,4,5);  
  7.          stream.forEach(s -> System.out.println(s));  
  8.      }  
  9. }  
Output



And:
  1. import java.util.stream.*;  
  2. public class sd2 
  3. {  
  4.      public static void main(String[] args)  
  5.      {  
  6.          Stream<Integer> stream = Stream.of( new Integer[]{1,2,3,4,5} );  
  7.          stream.forEach(s -> System.out.println(s));  
  8.      }  
  9. }  
Output



Using "stream.genrate":
  1. import java.util.stream.*;  
  2. import java.util.*;  
  3. public class sd3   
  4. {  
  5.      public static void main(String[] args)  
  6.      {  
  7.          Stream<Date> stream = Stream.generate(() -> { return new Date();});  
  8.          stream.forEach(s -> System.out.println(s));  
  9.            
  10.      }  
  11. }  

4) Compact Profiles

Another useful feature of Java 8 is Compact Profile. In Compact Profile, the applications that do not require the entire Java platform reduces the use of the memory footprint or we can say that it allows us to deploy only those components that are required by the application. The Java SE 8 compiler (javac) allows the application to be compiled using one of the new supported features called "-profile".

There are 3 types of compact profiles named Compact1, Compact2 and Compact3. And the next one is the Superset of the last one, in other words Compact3 is a superset of Compact2 and correspondingly Compact 2 is the superset of Compact1.

Compact 1 Compact 2 Compact 3
Core JDBC Security
Security RMI JMX
Serialization XML JAXP JNDI
Networking   XML JAXP
Ref Objects   Management
Regular Expressions   Instrumentation
Date and Time    
Input/Output    
Collections    
Logging    
Concurrency    
Reflection    
JAR    
ZIP    
Versioning    
Internationalization    
JNI    
Overriden Mechanism    
Extension Mechanism    
Scripting    

Using the jdeps Tool

Method Name Description
-s Print dependency summary.
-v Print additional info.
-V <level> Print package-level or class-level dependencies.
-c <path> Specify where to find class file.
-p <pkg name> Restrict analysis to classes matching pattern.
-e <regex> Restrict analysis to packages matching pattern.
-P Show profile or the file containing a package.
-R Version information.

Example

  1. class helloworld  
  2. {  
  3.    public static void main(String args[])  
  4.    {  
  5.        System.out.println("Hello World");  
  6.    }  
  7. }  
Output: jdps -P helloworld.class


5) Optional

One of the amazing features of Java 8 is Optional that is present in the util package. Using the "java.util.optional" class we can deal with null pointer exceptions or we can say that we can make our code more readable then before and can protect our code from null pointer exceptions. According to my view, it is very confusing to use Optional in the starting period.

Let's use an example of null pointer exception: This program throws null pointer exceptions.

  1. import java.util.*;  
  2. public class opt_demo  
  3. {  
  4.     public String getNullString()  
  5.     {  
  6.        return(null);  
  7.     }  
  8.     public static void main(String[] args)  
  9.     {  
  10.        opt_demo obj=new opt_demo();  
  11.        String nullString=obj.getNullString();  
  12.        System.out.println(nullString);  
  13.     }  
  14. }  
Output


So, here is the solution with the use of optional: Here Optional is used.

  1. import java.util.Optional;  
  2. public class opt_test  
  3. {  
  4.     public Optional<String> getOptionalNullString()  
  5.        {  
  6.           return(null);  
  7.        }  
  8.        public static void main(String[] args)  
  9.        {  
  10.           opt_test obj=new opt_test();  
  11.           Optional<String> optionalString=obj.getOptionalNullString();  
  12.           System.out.println(optionalString);  
  13.        }  
  14. }  
Output


Some methods of Optional

Method Name Uses Work
empty public static <T>Optional<T>empty() Returns an empty Optional instance
of public static <T>Optional<T>of(T value) Returns specified non-null value
ofNullable public static <T>Optional<T>ofNullable(T value) Returns specified value, otherwise returns an empty optional
filter public Optional<T>filter(Predicate<? super T>predicate) If value is present and matches, return value describing Optional, else returns an empty optional
map public <U>Optional<U>map(function<? super T,? estends U>mapper) If present, apply given mapping function and if non-null, return result describing Optional, else returns an empty optional
flatMap public <U>Optional<U>flatMap(function<? super T,Optional< U>>mapper) It is similar to map() method, but flatMap doesn't bind it with an additional Optional


 6) New Date/Time API(JSR 310)

In Java SE 8, another feature that was introduced is the Date/Time package, "java.time" and it was developed under JSR 310(API) that provides us a extensive model for Date and Time. It contains all the classes for date/time, time zones, duration, instants and clock manipulation. It is based on the ISO calendar system that is commonly used for global calenders. Some of its class is described below:

Class Description
LocalDate Shows only a date (without offset or zone)
LocalTime Shows only time (without offset or zone)
LocalDateTime Shows date and time (without offset or zone)
OffsetDate Shows a date with an offset such as +05:30
OffsetTime Shows time with an offset such as +05:30
OffsetDateTime Shows date and time with an offset such as +05:30
ZonedDateTime Shows date and time with offset and time zone
YearMonth Shows a year and month
MonthDay Shows month and day
Period Shows a defined time (such as 1 week)

Example

  1. import java.time.*;  
  2. import java.time.format.DateTimeFormatter;  
  3. public class date_time   
  4. {  
  5.     public static void main(String[] args)   
  6.     {  
  7.         System.out.println("");  
  8.         //Current Date  
  9.         LocalDate today = LocalDate.now();  
  10.         System.out.println("Current Date="+today);  
  11.         //Specific Date  
  12.         LocalDate firstDay_2015 = LocalDate.of(2015, Month.JANUARY, 1);  
  13.         System.out.println("Specific Date="+firstDay_2015);  
  14.         //Current date in IST format  
  15.         LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));  
  16.         System.out.println("Current Date in IST="+todayKolkata);  
  17.         System.out.println("");  
  18.         //Current Time  
  19.         LocalTime time = LocalTime.now();  
  20.         System.out.println("Current Time="+time);  
  21.         //Specific Time   
  22.         LocalTime specificTime = LocalTime.of(12,20,25,40);  
  23.         System.out.println("Specific Time of Day="+specificTime);  
  24.         //Current Time in IST format  
  25.         LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));  
  26.         System.out.println("Current Time in IST="+timeKolkata);  
  27.         System.out.println("");  
  28.         //Format examples  
  29.         LocalDate date = LocalDate.now();  
  30.         //default format  
  31.         System.out.println("Default format of LocalDate="+date);  
  32.         //specific format  
  33.         System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));  
  34.         System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));  
  35.         System.out.println("");  
  36.         LocalDateTime dateTime = LocalDateTime.now();  
  37.         //default format  
  38.         System.out.println("Default format of LocalDateTime="+dateTime);  
  39.         //specific format  
  40.         System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));  
  41.         System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));  
  42.     }  
  43. }  
Output


7) Nashorn JavaScript Engine

Java SE 8 introduced another interesting feature, Nashrorn (pronounced "nass-horn"). Nashrorn is a German word for Rhinoceros. JavaScript Engine. For the Java Virtual Machine (JVM) Nashrorn replaces Rhino as the default JavaScript Engine. We can implement it in two ways, one way is to use it programmatically in a Java program and second, using the jjs command line (used for learning purposes) tool located in $JAVA_HOME/bin.

The following are some ways to implement the Nashron JavaScript Engine:

  • JavaScript Functions from Java
  • Java Methods from JavaScripts
  • ScriptObjectMirror
  • Language Extensions

A simple example of "jjs" using the command line

Output


Now, using Nashorn embedded with Java :

  1. import javax.script.ScriptEngine;  
  2. import javax.script.ScriptEngineManager;  
  3. import javax.script.ScriptException;  
  4. public class nash_js   
  5. {  
  6.     public static void main(String[] args) throws Exception   
  7.     {  
  8.         ScriptEngineManager scriptEngineManager = new ScriptEngineManager();  
  9.         ScriptEngine scriptEngine = scriptEngineManager  
  10.                 .getEngineByName("nashorn");  
  11.         try   
  12.         {  
  13.             scriptEngine.eval("print('Example of Nashorn embedded in java!');");  
  14.         }  
  15.         catch (final ScriptException se)   
  16.         {  
  17.             se.printStackTrace();  
  18.         }  
  19.     }  
  20. }  
Output


Summary

The summarized version of the preceding article is that Java SE 8 introduced many features for making Java more productive than ever before and the entire credit goes to Oracle's implementations. Features like Lambda Expressions, Method References, Streams, Compact Profile, the Optional (JSR 310) API and the Nashorn JavaScript Engine are some very useful features. By using a "lambda expression" we can code in the same place where we will use it. Using a "method reference" we can use a method without executing it. Using "Streams" we can process a collection of objects in a different way. Using "compact profile" we can run only the code that is required by the Java environment. Using "optional" we can handle null pointer exceptions. Using the "date/time API" we can get dates/times in many ways and formats. Using the "Nashorn JavaScript Engine" we can run JavaScript embedded into Java code.