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 features at the language level support in Java 8.   Simply, it is a method without declaration, like access modifier, declaration, return value, and name.  It enables 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 the 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.  public static void main(String[] a) {  
  6.   List < Integer > list = Arrays.asList(12345);  
  7.   System.out.println("List Contain");  
  8.   evaluate(list, (n) -> true);  
  9.   System.out.println("Even Numbers are:");  
  10.   evaluate(list, (n) -> n % 2 == 0);  
  11.   System.out.println("Odd Numbers are:");  
  12.   evaluate(list, (n) -> n % 2 == 1);  
  13.   System.out.println("Numbers smaller than 3 are:");  
  14.   evaluate(list, (n) -> n < 5);  
  15.   System.out.println("Numbers greater than 3 are:");  
  16.   evaluate(list, (n) -> n > 5);  
  17.  }  
  18.  public static void evaluate(List < Integer > list, Predicate < Integer > predicate) {  
  19.   for (Integer n: list) {  
  20.    if (predicate.test(n)) {  
  21.     System.out.println(n + " ");  
  22.    }  
  23.   }  
  24.  }  
  25. }   
Output
 
 
 

2) Method Reference
 

The other important feature in Java 8, is Method Reference, which 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.  public static void main(String[] args) {  
  4.   String s = "Example of method references ";  
  5.   print(s::toString);  
  6.   print(() -> s.toString());  
  7.   print(new Supplier < String > () {  
  8.    public String get() {  
  9.     return s.toString();  
  10.    }  
  11.   });  
  12.  }  
  13.  public static void print(Supplier < String > supplier) {  
  14.   System.out.println(supplier.get());  
  15.  }  
  16. }   
Output
 
 

3) Streams
 

It is located in the "java.util.stream" package.  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, reduces 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.  public static void main(String[] args) {  
  4.   Stream < Integer > stream = Stream.of(12345);  
  5.   stream.forEach(s -> System.out.println(s));  
  6.  }  
  7. }   
Output
 
 
And:
  1. import java.util.stream.*;  
  2. public class sd2 {  
  3.  public static void main(String[] args) {  
  4.   Stream < Integer > stream = Stream.of(new Integer[] {  
  5.    1,  
  6.    2,  
  7.    3,  
  8.    4,  
  9.    5  
  10.   });  
  11.   stream.forEach(s -> System.out.println(s));  
  12.  }  
  13. }  
Output
 
 
Using "stream.genrate":
  1. import java.util.stream.*;  
  2. import java.util.*;  
  3. public class sd3 {  
  4.  public static void main(String[] args) {  
  5.   Stream < Date > stream = Stream.generate(() -> {  
  6.    return new Date();  
  7.   });  
  8.   stream.forEach(s -> System.out.println(s));  
  9.  }  
  10. }    

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 newly 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 the 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.  public static void main(String args[]) {  
  3.   System.out.println("Hello World");  
  4.  }  
  5. }   
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.  public String getNullString() {  
  4.   return (null);  
  5.  }  
  6.  public static void main(String[] args) {  
  7.   opt_demo obj = new opt_demo();  
  8.   String nullString = obj.getNullString();  
  9.   System.out.println(nullString);  
  10.  }  
  11. }   
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.  public Optional < String > getOptionalNullString() {  
  4.   return (null);  
  5.  }  
  6.  public static void main(String[] args) {  
  7.   opt_test obj = new opt_test();  
  8.   Optional < String > optionalString = obj.getOptionalNullString();  
  9.   System.out.println(optionalString);  
  10.  }  
  11. }   
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) which provides us an extensive model for Date and Time.  It contains all the classes for date/time, such as, 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.  public static void main(String[] args) {  
  5.   System.out.println("");  
  6.   LocalDate today = LocalDate.now();  
  7.   System.out.println("Current Date=" + today);  
  8.   LocalDate firstDay_2015 = LocalDate.of(2015, Month.JANUARY, 1);  
  9.   System.out.println("Specific Date=" + firstDay_2015);  
  10.   LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));  
  11.   System.out.println("Current Date in IST=" + todayKolkata);  
  12.   System.out.println("");  
  13.   LocalTime time = LocalTime.now();  
  14.   System.out.println("Current Time=" + time);  
  15.   LocalTime specificTime = LocalTime.of(12202540);  
  16.   System.out.println("Specific Time of Day=" + specificTime);  
  17.   LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));  
  18.   System.out.println("Current Time in IST=" + timeKolkata);  
  19.   System.out.println("");  
  20.   LocalDate date = LocalDate.now();  
  21.   System.out.println("Default format of LocalDate=" + date);  
  22.   System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));  
  23.   System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));  
  24.   System.out.println("");  
  25.   LocalDateTime dateTime = LocalDateTime.now();  
  26.   System.out.println("Default format of LocalDateTime=" + dateTime);  
  27.   System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));  
  28.   System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));  
  29.  }  
  30. }   
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.  public static void main(String[] args) throws Exception {  
  6.   ScriptEngineManager scriptEngineManager = new ScriptEngineManager();  
  7.   ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("nashorn");  
  8.   try {  
  9.    scriptEngine.eval("print('Example of Nashorn embedded in java!');");  
  10.   } catch (final ScriptException se) {  
  11.    se.printStackTrace();  
  12.   }  
  13.  }  
  14. }   
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.


Similar Articles