Java 8 Features Tutorial – The ULTIMATE Guide (PDF Download & video)
EDITORIAL NOTE: In this post, we feature a comprehensive Java 8 Features Tutorial. It’s been a while since Java 8 is out in the public and everything points to the fact that this is a really major release.
We have provided an abundance of tutorials here at Java Code Geeks, likePlaying with Java 8 – Lambdas and Concurrency,Java 8 Date Time API Tutorial : LocalDateTime andAbstract Class Versus Interface in the JDK 8 Era.
We also referenced15 Must Read Java 8 Tutorials from other sources. Of course, we examined some of the shortfalls also, likeThe Dark Side of Java 8.
You can also check this tutorial in the following video:
Now, it is time to gather all the major Java 8 features under one reference post for your reading pleasure. Enjoy!
Table Of Contents
1. Introduction
With no doubts,Java 8 release is the greatest thing in the Java world since Java 5 (released quite a while ago, back in 2004). It brings tons of new features to the Java as a language, its compiler, libraries, tools and the JVM (Java virtual machine) itself. In this tutorial we are going to take a look on all these changes and demonstrate the different usage scenarios on real examples.
The tutorial consists of several parts where each one touches the specific side of the platform:
- language
- compiler
- libraries
- tools
- runtime (JVM)
2. New Features in Java language
Java 8 is by any means a major release. One might say it took so long to finalize in order to implement the features every Java developer was looking for. In this section we are going to cover most of them.
2.1. Lambdas and Functional Interfaces
Lambdas (also known as closures) are the biggest and most awaited language change in the whole Java 8 release. They allow us to treat functionality as a method argument (passing functions around), or treat a code as data: the concepts everyfunctional developer is very familiar with. Many languages on JVM platform (Groovy,Scala, …) have had lambdas since day one, but Java developers had no choice but hammer the lambdas with boilerplate anonymous classes.
Lambdas design discussions have taken a lot of time and community efforts. But finally, the trade-offs have been found, leading to new concise and compact language constructs. In its simplest form, a lambda could be represented as a comma-separated list of parameters, the–> symbol and the body. For example:
1 | Arrays.asList("a","b","d").forEach( e -> System.out.println( e ) ); |
Please notice the type of argumente is being inferred by the compiler. Alternatively, you may explicitly provide the type of the parameter, wrapping the definition in brackets. For example:
1 | Arrays.asList("a","b","d").forEach( ( String e ) -> System.out.println( e ) ); |
In case lambda’s body is more complex, it may be wrapped into square brackets, as the usual function definition in Java. For example:
1 2 3 4 | Arrays.asList("a","b","d").forEach( e -> { System.out.print( e ); System.out.print( e );} ); |
Lambdas may reference the class members and local variables (implicitly making them effectivelyfinalif they are not). For example, those two snippets are equivalent:
1 2 3 | String separator =",";Arrays.asList("a","b","d").forEach( ( String e ) -> System.out.print( e + separator ) ); |
And:
1 2 3 | finalString separator =",";Arrays.asList("a","b","d").forEach( ( String e ) -> System.out.print( e + separator ) ); |
Lambdas may return a value. The type of the return value will be inferred by compiler. Thereturn statement is not required if the lambda body is just a one-liner. The two code snippets below are equivalent:
1 | Arrays.asList("a","b","d").sort( ( e1, e2 ) -> e1.compareTo( e2 ) ); |
And:
1 2 3 4 | Arrays.asList("a","b","d").sort( ( e1, e2 ) -> { intresult = e1.compareTo( e2 ); returnresult;} ); |
Language designers put a lot of thought on how to make already existing functionality lambda-friendly. As a result, the concept offunctional interfaces has emerged. The function interface is an interface with just one single method. As such, it may be implicitly converted to a lambda expression. Thejava.lang.Runnable andjava.util.concurrent.Callable are two great examples of functional interfaces. In practice, the functional interfaces are fragile: if someone adds just one another method to the interface definition, it will not be functional anymore and compilation process will fail. To overcome this fragility and explicitly declare the intent of the interface as being functional, Java 8 adds special annotation @FunctionalInterface (all existing interfaces in Java library have been annotated with @FunctionalInterface as well). Let us take a look on this simple functional interface definition:
1 2 3 4 | @FunctionalInterfacepublicinterfaceFunctional { voidmethod();} |
One thing to keep in mind:default and static methods do not break the functional interface contract and may be declared:
1 2 3 4 5 6 7 | @FunctionalInterfacepublicinterfaceFunctionalDefaultMethods { voidmethod(); defaultvoiddefaultMethod() { } } |
Lambdas are the largest selling point of Java 8. It has all the potential to attract more and more developers to this great platform and provide state of the art support for functional programming concepts in pure Java. For more details please refer toofficial documentation.
2.2. Interface’s Default and Static Methods
Java 8 extends interface declarations with two new concepts: default and static methods.Default methods make interfaces somewhat similar to traits but serve a bit different goal. They allow adding new methods to existing interfaces without breaking the binary compatibility with the code written for older versions of those interfaces.
The difference between default methods and abstract methods is that abstract methods are required to be implemented. But default methods are not. Instead, each interface must provide so called default implementation and all the implementers will inherit it by default (with a possibility to override this default implementation if needed). Let us take a look on example below.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | privateinterfaceDefaulable { // Interfaces now allow default methods, the implementer may or // may not implement (override) them. defaultString notRequired() { return"Default implementation"; } } privatestaticclassDefaultableImplimplementsDefaulable {} privatestaticclassOverridableImplimplementsDefaulable { @Override publicString notRequired() { return"Overridden implementation"; }} |
The interfaceDefaulable declares a default methodnotRequired()using keyworddefault as part of the method definition. One of the classes,DefaultableImpl, implements this interface leaving the default method implementation as-is. Another one,OverridableImpl , overrides the default implementation and provides its own.
Another interesting feature delivered by Java 8 is that interfaces can declare (and provide implementation) of static methods. Here is an example.
1 2 3 4 5 6 | privateinterfaceDefaulableFactory { // Interfaces now allow static methods staticDefaulable create( Supplier< Defaulable > supplier ) { returnsupplier.get(); }} |
The small code snippet below glues together the default methods and static methods from the examples above.
1 2 3 4 5 6 7 | publicstaticvoidmain( String[] args ) { Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new); System.out.println( defaulable.notRequired() ); defaulable = DefaulableFactory.create( OverridableImpl::new); System.out.println( defaulable.notRequired() );} |
The console output of this program looks like that:
1 2 | Default implementationOverridden implementation |
Default methods implementation on JVM is very efficient and is supported by the byte code instructions for method invocation. Default methods allowed existing Java interfaces to evolve without breaking the compilation process. The good examples are the plethora of methods added tojava.util.Collection interface:stream(),parallelStream(),forEach(),removeIf(), …
Though being powerful, default methods should be used with a caution: before declaring method as default it is better to think twice if it is really needed as it may cause ambiguity and compilation errors in complex hierarchies. For more details please refer toofficial documentation.
2.3. Method References
Method references provide the useful syntax to refer directly to exiting methods or constructors of Java classes or objects (instances). With conjunction ofLambdas expressions, method references make the language constructs look compact and concise, leaving off boilerplate.
Below, considering the classCaras an example of different method definitions, let us distinguish four supported types of method references.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | publicstaticclassCar { publicstaticCar create(finalSupplier< Car > supplier ) { returnsupplier.get(); } publicstaticvoidcollide(finalCar car ) { System.out.println("Collided "+ car.toString() ); } publicvoidfollow(finalCar another ) { System.out.println("Following the "+ another.toString() ); } publicvoidrepair() { System.out.println("Repaired "+this.toString() ); }} |
The first type of method references is constructor reference with the syntaxClass::new or alternatively, for generics,Class< T >::new. Please notice that the constructor has no arguments.
1 2 | finalCar car = Car.create( Car::new);finalList< Car > cars = Arrays.asList( car ); |
The second type is reference to static method with the syntaxClass::static_method. Please notice that the method accepts exactly one parameter of typeCar.
1 | cars.forEach( Car::collide ); |
The third type is reference to instance method of arbitrary object of specific type with the syntaxClass::method. Please notice, no arguments are accepted by the method.
1 | cars.forEach( Car::repair ); |
And the last, fourth type is reference to instance method of particular class instance the syntaxinstance::method. Please notice that method accepts exactly one parameter of typeCar.
1 2 | finalCar police = Car.create( Car::new);cars.forEach( police::follow ); |
Running all those examples as a Java program produces following output on a console (the actualCar instances might be different):
1 2 3 | Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197dRepaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197dFollowing the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d |
For more examples and details on method references, please refer toofficial documentation.
2.4. Repeating annotations
Since Java 5 introduced theannotations support, this feature became very popular and is very widely used. However, one of the limitations of annotation usage was the fact that the same annotation cannot be declared more than once at the same location. Java 8 breaks this rule and introduced the repeating annotations. It allows the same annotation to be repeated several times in place it is declared.
The repeating annotations should be themselves annotated with @Repeatable annotation. In fact, it is not a language change but more a compiler trick as underneath the technique stays the same. Let us take a look on quick example:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | packagecom.javacodegeeks.java8.repeatable.annotations;importjava.lang.annotation.ElementType;importjava.lang.annotation.Repeatable;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;publicclassRepeatingAnnotations { @Target( ElementType.TYPE ) @Retention( RetentionPolicy.RUNTIME ) public@interfaceFilters { Filter[] value(); } @Target( ElementType.TYPE ) @Retention( RetentionPolicy.RUNTIME ) @Repeatable( Filters.class) public@interfaceFilter { String value(); }; @Filter("filter1") @Filter("filter2") publicinterfaceFilterable { } publicstaticvoidmain(String[] args) { for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class) ) { System.out.println( filter.value() ); } }} |
As we can see, there is an annotation classFilter annotated with @Repeatable( Filters.class ). TheFiltersis just a holder ofFilter annotations but Java compiler tries hard to hide its presence from the developers. As such, the interfaceFilterable hasFilter annotation defined twice (with no mentions ofFilters).
Also, the Reflection API provides new methodgetAnnotationsByType() to return repeating annotations of some type (please notice that Filterable.class.getAnnotation( Filters.class ) will return the instance ofFiltersinjected by the compiler).
The program output looks like that:
1 2 | filter1filter2 |
For more details please refer toofficial documentation.
2.5. Better Type Inference
Java 8 compiler has improved a lot on type inference. In many cases the explicit type parameters could be inferred by compiler keeping the code cleaner. Let us take a look on one of the examples.
01 02 03 04 05 06 07 08 09 10 11 | packagecom.javacodegeeks.java8.type.inference;publicclassValue< T > { publicstatic< T > T defaultValue() { returnnull; } publicT getOrDefault( T value, T defaultValue ) { return( value !=null) ? value : defaultValue; }} |
And here is the usage ofValue< String > type.
1 2 3 4 5 6 7 8 | packagecom.javacodegeeks.java8.type.inference;publicclassTypeInference { publicstaticvoidmain(String[] args) { finalValue< String > value =newValue<>(); value.getOrDefault("22", Value.defaultValue() ); }} |
The type parameter ofValue.defaultValue()is inferred and is not required to be provided. In Java 7, the same example will not compile and should be rewritten toValue.< String >defaultValue().
2.6. Extended Annotations Support
Java 8 extends the context where annotation might be used. Now, it is possible to annotate mostly everything: local variables, generic types, super-classes and implementing interfaces, even the method’s exceptions declaration. Couple of examples are show below.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | packagecom.javacodegeeks.java8.annotations;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;importjava.util.ArrayList;importjava.util.Collection;publicclassAnnotations { @Retention( RetentionPolicy.RUNTIME ) @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } ) public@interfaceNonEmpty { } publicstaticclassHolder<@NonEmptyT >extends@NonEmptyObject { publicvoidmethod()throws@NonEmptyException { } } @SuppressWarnings("unused") publicstaticvoidmain(String[] args) { finalHolder< String > holder =new@NonEmptyHolder< String >(); @NonEmptyCollection<@NonEmptyString > strings =newArrayList<>(); }} |
TheElementType.TYPE_USEandElementType.TYPE_PARAMETERare two new element types to describe the applicable annotation context. TheAnnotation Processing API also underwent some minor changes to recognize those new type annotations in the Java programming language.
3. New Features in Java compiler
3.1. Parameter names
Literally for ages Java developers are inventing different ways to preservemethod parameter names in Java byte-code and make them available at runtime (for example,Paranamer library). And finally, Java 8 bakes this demanding feature into the language (using Reflection API andParameter.getName() method) and the byte-code (using newjavac compiler argument–parameters).
01 02 03 04 05 06 07 08 09 10 11 12 13 | packagecom.javacodegeeks.java8.parameter.names;importjava.lang.reflect.Method;importjava.lang.reflect.Parameter;publicclassParameterNames { publicstaticvoidmain(String[] args)throwsException { Method method = ParameterNames.class.getMethod("main", String[].class); for(finalParameter parameter: method.getParameters() ) { System.out.println("Parameter: "+ parameter.getName() ); } }} |
If you compile this class without using–parameters argument and then run this program, you will see something like that:
1 | Parameter: arg0 |
With–parameters argument passed to the compiler the program output will be different (the actual name of the parameter will be shown):
1 | Parameter: args |
Forexperienced Maven users the–parameters argument could be added to the compiler using configuration section of themaven-compiler-plugin:
01 02 03 04 05 06 07 08 09 10 | <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <compilerArgument>-parameters</compilerArgument> <source>1.8</source> <target>1.8</target> </configuration></plugin> |
LatestEclipse Kepler SR2 release with Java 8 (please check outthis download instructions) support provides useful configuration option to control this compiler setting as the picture below shows.
Additionally, to verify the availability of parameter names, there is a handy methodisNamePresent() provided byParameterclass.
4. New Features in Java libraries
Java 8 adds a lot of new classes and extends existing ones in order to provide better support of modern concurrency, functional programming, date/time, and many more.
4.1. Optional
ThefamousNullPointerException is by far the most popular cause of Java application failures. Long time ago the greatGoogle Guava project introduced theOptionals as a solution toNullPointerExceptions, discouraging codebase pollution withnull checks and encouraging developers to write cleaner code. Inspired byGoogle Guava, theOptional is now a part of Java 8 library.
Optionalis just a container: it can hold avalue of some typeT or just benull. It provides a lot of useful methods so the explicitnull checks have no excuse anymore. Please refer toofficial Java 8 documentation for more details.
We are going to take a look on two small examples ofOptional usages: with thenullable value and with the value which does not allownulls.
1 2 3 4 | Optional<String> fullName = Optional.ofNullable( null );System.out.println( "Full Name is set? " + fullName.isPresent() ); System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) ); |
TheisPresent()method returnstrue if this instance ofOptional has non-null value andfalse otherwise. TheorElseGet()method provides the fallback mechanism in caseOptional hasnull value by accepting the function to generate the default one. Themap() method transforms the currentOptional’s value and returns the newOptional instance. TheorElse()method is similar toorElseGet()but instead of function it accepts the default value. Here is the output of this program:
1 2 3 | Full Name is set?falseFull Name: [none]Hey Stranger! |

Thank you!
We will contact you soon.
Let us briefly look on another example:
1 2 3 4 5 | Optional<String> firstName = Optional.of( "Tom" );System.out.println( "First Name is set? " + firstName.isPresent() ); System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );System.out.println(); |
And here is the output:
1 2 3 | First Name is set?trueFirst Name: TomHey Tom! |
For more details please refer toofficial documentation.
4.2. Streams
The newlyadded Stream API (java.util.stream) introduces real-world functional-style programming into the Java. This is by far the most comprehensive addition to Java library intended to make Java developers significantly more productive by allowing them to write effective, clean, and concise code.
Stream API makes collections processing greatly simplified (but it is not limited to Java collections only as we will see later). Let us take start off with simple class called Task.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | publicclassStreams { privateenumStatus { OPEN, CLOSED }; privatestaticfinalclassTask { privatefinalStatus status; privatefinalInteger points; Task(finalStatus status,finalInteger points ) { this.status = status; this.points = points; } publicInteger getPoints() { returnpoints; } publicStatus getStatus() { returnstatus; } @Override publicString toString() { returnString.format("[%s, %d]", status, points ); } }} |
Task has some notion of points (or pseudo-complexity) and can be eitherOPEN orCLOSED. And then let us introduce a small collection of tasks to play with.
1 2 3 4 5 | finalCollection< Task > tasks = Arrays.asList( newTask( Status.OPEN,5), newTask( Status.OPEN,13), newTask( Status.CLOSED,8)); |
The first question we are going to address is how many points in total allOPEN tasks have? Up to Java 8, the usual solution for it would be some sort offoreach iteration. But in Java 8 the answers is streams: a sequence of elements supporting sequential and parallel aggregate operations.
1 2 3 4 5 6 7 8 | // Calculate total points of all active tasks using sum()finallongtotalPointsOfOpenTasks = tasks .stream() .filter( task -> task.getStatus() == Status.OPEN ) .mapToInt( Task::getPoints ) .sum(); System.out.println("Total points: "+ totalPointsOfOpenTasks ); |
And the output on the console looks like that:
1 | Total points:18 |
There are a couple of things going on here. Firstly, the tasks collection is converted to its stream representation. Then, thefilter operation on stream filters out allCLOSED tasks. On next step, themapToInt operation converts the stream ofTasks to the stream ofIntegers usingTask::getPoints method of the each task instance. And lastly, all points are summed up usingsum method, producing the final result.
Before moving on to the next examples, there are some notes to keep in mind about streams (more details here). Stream operations are divided into intermediate and terminal operations.
Intermediate operations return a new stream. They are always lazy, executing an intermediate operation such asfilter does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate
Terminal operations, such asforEach orsum, may traverse the stream to produce a result or a side-effect. After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used. In almost all cases, terminal operations are eager, completing their traversal of the underlying data source.
Yet another value proposition of the streams is out-of-the box support of parallel processing. Let us take a look on this example, which does sums the points of all the tasks.
1 2 3 4 5 6 7 8 | // Calculate total points of all tasksfinaldoubletotalPoints = tasks .stream() .parallel() .map( task -> task.getPoints() )// or map( Task::getPoints ) .reduce(0, Integer::sum ); System.out.println("Total points (all tasks): "+ totalPoints ); |
It is very similar to the first example except the fact that we try to process all the tasks inparallel and calculate the final result usingreduce method.
Here is the console output:
1 | Total points (all tasks):26.0 |
Often, there is a need to performing a grouping of the collection elements by some criteria. Streams can help with that as well as an example below demonstrates.
1 2 3 4 5 | // Group tasks by their statusfinalMap< Status, List< Task > > map = tasks .stream() .collect( Collectors.groupingBy( Task::getStatus ) );System.out.println( map ); |
The console output of this example looks like that:
1 | {CLOSED=[[CLOSED,8]], OPEN=[[OPEN,5], [OPEN,13]]} |
To finish up with the tasks example, let us calculate the overall percentage (or weight) of each task across the whole collection, based on its points.
01 02 03 04 05 06 07 08 09 10 11 12 | // Calculate the weight of each tasks (as percent of total points)finalCollection< String > result = tasks .stream() // Stream< String > .mapToInt( Task::getPoints ) // IntStream .asLongStream() // LongStream .mapToDouble( points -> points / totalPoints ) // DoubleStream .boxed() // Stream< Double > .mapToLong( weigth -> (long)( weigth *100) )// LongStream .mapToObj( percentage -> percentage +"%") // Stream< String> .collect( Collectors.toList() ); // List< String > System.out.println( result ); |
The console output is just here:
1 | [19%,50%,30%] |
And lastly, as we mentioned before, the Stream API is not only about Java collections. The typical I/O operations like reading the text file line by line is a very good candidate to benefit from stream processing. Here is a small example to confirm that.
1 2 3 4 | finalPath path =newFile( filename ).toPath();try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) { lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );} |
TheonClose method called on the stream returns an equivalent stream with an additional close handler. Close handlers are run when theclose() method is called on the stream.
Stream API together withLambdas andMethod References baked byInterface’s Default and Static Methods is the Java 8 response to the modern paradigms in software development. For more details, please refer toofficial documentation.
4.3. Date/Time API (JSR 310)
Java 8 makes one more take on date and time management by deliveringNew Date-Time API (JSR 310). Date and time manipulation is being one of the worst pain points for Java developers. The standardjava.util.Date followed byjava.util.Calendarhasn’t improved the situation at all (arguably, made it even more confusing).
That is howJoda-Time was born: the great alternative date/time API for Java. The Java 8’sNew Date-Time API (JSR 310) was heavily influenced byJoda-Time and took the best of it. The newjava.time package containsall the classes for date, time, date/time, time zones, instants, duration, and clocks manipulation. In the design of the API the immutability has been taken into account very seriously: no change allowed (the tough lesson learnt from java.util.Calendar). If the modification is required, the new instance of respective class will be returned.
Let us take a look on key classes and examples of their usages. The first class isClock which provides access to the current instant, date and time using a time-zone.Clock can be used instead ofSystem.currentTimeMillis() andTimeZone.getDefault().
1 2 3 4 | // Get the system clock as UTC offsetfinalClock clock = Clock.systemUTC();System.out.println( clock.instant() );System.out.println( clock.millis() ); |
The sample output on a console:
1 2 | 2014-04-12T15:19:29.282Z1397315969360 |
Other new classes we are going to look at areLocaleDate andLocalTime.LocaleDateholds only the date part without a time-zone in the ISO-8601 calendar system. Respectively,LocaleTimeholds only the time part without time-zone in the ISO-8601 calendar system. BothLocaleDateand LocaleTimecould be created fromClock.
01 02 03 04 05 06 07 08 09 10 11 12 13 | // Get the local date and local timefinalLocalDate date = LocalDate.now();finalLocalDate dateFromClock = LocalDate.now( clock ); System.out.println( date );System.out.println( dateFromClock ); // Get the local date and local timefinalLocalTime time = LocalTime.now();finalLocalTime timeFromClock = LocalTime.now( clock ); System.out.println( time );System.out.println( timeFromClock ); |
The sample output on a console:
1 2 3 4 | 2014-04-122014-04-1211:25:54.56815:25:54.568 |
TheLocalDateTime combines togetherLocaleDate andLocalTime and holds a date with time but without a time-zone in the ISO-8601 calendar system. Aquick example is shown below.
1 2 3 4 5 6 | // Get the local date/timefinalLocalDateTime datetime = LocalDateTime.now();finalLocalDateTime datetimeFromClock = LocalDateTime.now( clock ); System.out.println( datetime );System.out.println( datetimeFromClock ); |
The sample output on a console:
1 2 | 2014-04-12T11:37:52.3092014-04-12T15:37:52.309 |
If case you need a date/time for particular timezone, theZonedDateTimeis here to help. It holds a date with time and with a time-zone in the ISO-8601 calendar system. Here are a couple of examples for different timezones.
1 2 3 4 5 6 7 8 | // Get the zoned date/timefinalZonedDateTime zonedDatetime = ZonedDateTime.now();finalZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );finalZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of("America/Los_Angeles") ); System.out.println( zonedDatetime );System.out.println( zonedDatetimeFromClock );System.out.println( zonedDatetimeFromZone ); |
The sample output on a console:
1 2 3 | 2014-04-12T11:47:01.017-04:00[America/New_York]2014-04-12T15:47:01.017Z2014-04-12T08:47:01.017-07:00[America/Los_Angeles] |
And finally, let us take a look onDuration class: an amount of time in terms of seconds and nanoseconds. It makes very easy to compute the different between two dates. Let us take a look on that.
1 2 3 4 5 6 7 | // Get duration between two datesfinalLocalDateTime from = LocalDateTime.of(2014, Month.APRIL,16,0,0,0);finalLocalDateTime to = LocalDateTime.of(2015, Month.APRIL,16,23,59,59);finalDuration duration = Duration.between( from, to );System.out.println("Duration in days: "+ duration.toDays() );System.out.println("Duration in hours: "+ duration.toHours() ); |
The example above computes the duration (in days and hours) between two dates,16 April 2014 and16 April 2015. Here is the sample output on a console:
1 2 | Duration in days:365Duration in hours:8783 |
The overall impression about Java 8’s new date/time API is very, very positive. Partially, because of the battle-proved foundation it is built upon (Joda-Time), partially because this time it was finally tackled seriously and developer voices have been heard. For more details please refer toofficial documentation.
4.4. Nashorn JavaScript engine
Java 8 comes with new Nashorn JavaScript engine which allows developing and running certain kinds of JavaScript applications on JVM. Nashorn JavaScript engine is just another implementation of javax.script.ScriptEngine and follows the same set of rules, permitting Java and JavaScript interoperability. Here is a small example.
1 2 3 4 5 | ScriptEngineManager manager =newScriptEngineManager();ScriptEngine engine = manager.getEngineByName("JavaScript"); System.out.println( engine.getClass().getName() );System.out.println("Result:"+ engine.eval("function f() { return 1; }; f() + 1;") ); |
The sample output on a console:
1 2 | jdk.nashorn.api.scripting.NashornScriptEngineResult:2 |
We will get back to the Nashorn later in the sectiondedicated to new Java tools.
4.5. Base64
Finally, thesupport of Base64 encoding has made its way into Java standard library with Java 8 release. It is very easy to use as following example shows off.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | packagecom.javacodegeeks.java8.base64;importjava.nio.charset.StandardCharsets;importjava.util.Base64;publicclassBase64s { publicstaticvoidmain(String[] args) { finalString text ="Base64 finally in Java 8!"; finalString encoded = Base64 .getEncoder() .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) ); System.out.println( encoded ); finalString decoded =newString( Base64.getDecoder().decode( encoded ), StandardCharsets.UTF_8 ); System.out.println( decoded ); }} |
The console output from program run shows both encoded and decoded text:
1 2 | QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==Base64finallyin Java8! |
There are also URL-friendly encoder/decoder and MIME-friendly encoder/decoder provided by the Base64 class (Base64.getUrlEncoder() /Base64.getUrlDecoder(),Base64.getMimeEncoder() /Base64.getMimeDecoder()).
4.6. Parallel Arrays
Java 8 release adds a lot of new methods to allow parallel arrays processing. Arguably, the most important one isparallelSort() which may significantly speedup the sorting on multicore machines. The following small example demonstrates this new method family (parallelXxx) in action.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | packagecom.javacodegeeks.java8.parallel.arrays;importjava.util.Arrays;importjava.util.concurrent.ThreadLocalRandom;publicclassParallelArrays { publicstaticvoidmain( String[] args ) { long[] arrayOfLong =newlong[20000]; Arrays.parallelSetAll( arrayOfLong, index -> ThreadLocalRandom.current().nextInt(1000000) ); Arrays.stream( arrayOfLong ).limit(10).forEach( i -> System.out.print( i +" ") ); System.out.println(); Arrays.parallelSort( arrayOfLong ); Arrays.stream( arrayOfLong ).limit(10).forEach( i -> System.out.print( i +" ") ); System.out.println(); }} |
This small code snippet uses methodparallelSetAll() to fill up arrays with 20000 random values. After that, theparallelSort() is being applied. The program outputs first 10 elements before and after sorting so to ensure the array is really ordered. The sample program output may look like that (please notice that array elements are randomly generated):
1 2 | Unsorted:591217891976443951424479766825351964242997642839119108552378Sorted:39220263268325607655678723793 |
4.7. Concurrency
New methods have been added to thejava.util.concurrent.ConcurrentHashMap class to support aggregate operations based on the newly added streams facility and lambda expressions. Also, new methods have been added to thejava.util.concurrent.ForkJoinPool class to support a common pool (check also ourfree course on Java concurrency).
The newjava.util.concurrent.locks.StampedLock class has been added to provide a capability-based lock with three modes for controlling read/write access (it might be considered as better alternative for infamousjava.util.concurrent.locks.ReadWriteLock).
New classes have been added to thejava.util.concurrent.atomicpackage:
- DoubleAccumulator
- DoubleAdder
- LongAccumulator
- LongAdder
5. New Java tools
Java 8 comes with new set of command line tools. In this section we are going to look over most interesting of them.
5.1. Nashorn engine: jjs
jjs is a command line based standalone Nashorn engine. It accepts a list of JavaScript source code files as arguments and runs them. For example, let us create a filefunc.js with following content:
1 2 3 4 5 | function f() { return1;};print( f() +1); |
To execute this fie from command, let us pass it as an argument tojjs:
1 | jjs func.js |
The output on the console will be:
1 | 2 |
For more details please refer toofficial documentation.
5.2. Class dependency analyzer: jdeps
jdeps is a really great command line tool. It shows the package-level or class-level dependencies of Java class files. It accepts.class file,a directory, or JAR file as an input. By default,jdeps outputs the dependencies to the system output (console).
As an example, let us take a look on dependencies report for the popularSpring Framework library. To make example short, let us analyze only one JAR file:org.springframework.core-3.0.5.RELEASE.jar.
1 | jdeps org.springframework.core-3.0.5.RELEASE.jar |
This command outputs quite a lot so we are going to look on the part of it. The dependencies are grouped by packages. If dependency is not available on a classpath, it is shown asnot found.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar) -> java.io -> java.lang -> java.lang.annotation -> java.lang.ref -> java.lang.reflect -> java.util -> java.util.concurrent -> org.apache.commons.logging not found -> org.springframework.asm not found -> org.springframework.asm.commons not found org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar) -> java.lang -> java.lang.annotation -> java.lang.reflect -> java.util |
For more details please refer toofficial documentation.
6. New Features in Java runtime (JVM)
ThePermGen spaceis gone and has been replaced with Metaspace (JEP 122). The JVM options-XX:PermSize and –XX:MaxPermSize have been replaced by-XX:MetaSpaceSize and-XX:MaxMetaspaceSize respectively.
7. Conclusions
The future is here: Java 8 moves this great platform forward by delivering the features to make developers much more productive. It is too early to move the production systems to Java 8 but in the next couples of months its adoption should slowly start growing. Nevertheless the time is right to start preparing your code bases to be compatible with Java 8 and to be ready to turn the switch once Java 8 proves to be safe and stable enough.
Now, you canDownload and Install Java Development Kit (JDK) 8.
As a confirmation of community Java 8 acceptance, recently Pivotal releasedSpring Framework 4.0.3 with production-ready Java 8 support.
If you enjoyed this, thensubscribe to our newsletter to enjoy weekly updates and complimentary whitepapers! Also, check out ourcourses for more advanced training!
You are welcome to contribute with your comments about the exciting new Java 8 features!
8. Resources
Some additional resources which discuss in depth different aspects of Java 8 features:
- Java 8 Tutorials on JCG Examples:https://examples.javacodegeeks.com/?s=java+8
- What’s New in JDK 8:http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
- The Java Tutorials:http://docs.oracle.com/javase/tutorial/
- WildFly 8, JDK 8, NetBeans 8, Java EE 7:http://blog.arungupta.me/2014/03/wildfly8-jdk8-netbeans8-javaee7-excellent-combo-enterprise-java/
- Java 8 Tutorial:http://winterbe.com/posts/2014/03/16/java-8-tutorial/
- JDK 8 Command-line Static Dependency Checker:http://marxsoftware.blogspot.ca/2014/03/jdeps.html
- The Illuminating Javadoc of JDK 8:http://marxsoftware.blogspot.ca/2014/03/illuminating-javadoc-of-jdk-8.html
- The Dark Side of Java 8:http://blog.jooq.org/2014/04/04/java-8-friday-the-dark-side-of-java-8/
- Installing Java™ 8 Support in Eclipse Kepler SR2:http://www.eclipse.org/downloads/java8/
- Java 8:http://www.baeldung.com/java8
- Oracle Nashorn. A Next-Generation JavaScript Engine for the JVM:http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html
Last updated on Feb. 24th, 2022

Thank you!
We will contact you soon.





