Simran Kaur Arora | 13 Dec, 2022

Top Java 8 Features (With Examples) You Need to Know [2024]


Java 8 is an older Java version, released in March 2014. But many programmers agree Java 8 is one of the most widely used Java releases. Indeed, it's so popular that Oracle extended its support until 2030. Plus, organizations continue releasing Java 8 courses and books.

Java 8 is popular for several reasons. First, it's released under a slightly different license to the newer versions. In addition, it has many new and useful features. The best way to explore them is to practice with Java projects. But if you want a quick rundown of the top Java 8 features, we’ve got you covered. 

Today, we’ll cover some of the most important Java 8 features and why they matter.

What's Changed in Java 8?

Java 8’s most interesting features are additions to the language itself, such as:

  • Lambdas and functional interfaces
  • Improved type inference
  • forEach() method an iterable interface
  • Method references
  • Repeating annotations

Java's libraries also experienced some new additions, including:

  • Base64
  • Date/Time API
  • Streams
  • Concurrency

Another useful Java 8 feature is the jdeps class dependency analyzer, which makes working on large features much easier.

Key New Java 8 Features With Examples

We could write a full book on new features of Java 8. But here, we'll focus on some of our favorites, along with some Java 8 programming examples to show you how they work. 

Developers already familiar with Java programming should find it easy to use these concepts and features in their code.

Lambda Expressions

Lambda expressions are a block of code that can take a parameter and return a value. They perform a similar function to a method, but they don't need a name. Moreover, you can use them within the body of an existing method. Lambda expressions are useful for making code shorter and easier to read.

A lambda expression can either take a single parameter, in this form:

parameter -> expression

Or, more than one parameter, in parentheses:

(first_parameter, second_parameter) -> expression

Lambda expressions can reduce the amount of code required for relatively simple processes. However, an expression must immediately return a value. It cannot be used with variables, if statements, or assignments. However, you can use a code block instead of an expression:

(first_parameter, second_parameter) -> { code to execute; }

If the code block must return something, then you must use the return command:

Here’s an example of a lambda expression:

import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
public class Java8Example {
public static void main(String args[]) {
List<String> shoppingList = new ArrayList<String>();
shoppingList.add("Onions");
shoppingList.add("Bread");
shoppingList.add("Flour");
shoppingList.add("Sugar");
shoppingList.add("Chicken");
Java8Example example = new Java8Example();
example.sortUsingLambda(shoppingList);
System.out.println(shoppingList);
}
private void sortUsingLambda(List<String> shoppingList) {
Collections.sort(shoppingList, (s1, s2) -> s1.compareTo(s2));
}
}

Improved Type Inference

Type inference is the way the Java compiler infers the type of any parameters sent after invoking a generic method. If a data type is unspecified, the compiler will use contextual information to infer what the type should be. 

Type inference wasn’t a new concept to Java 8. A limited version of this feature has been available for some time. However, it was improved for Java 8.

Type inference is useful because it keeps Java code clean and readable. In Java’s earliest versions, there was no type inference, meaning the data type had to be specified. 

For example:

List<Integer> ageInYears = new ArrayList<Integer>();

Java 7 introduced type inference, meaning the same list could be declared using:

ageInYears = new ArrayList<>();

When we add an age to the list, the compiler automatically infers that the age is an integer rather than, for example, a string.

ageInYears.add(30);

This was further expanded in Java 8 to allow us to call a generic method without having to specify the type:

import java.util.ArrayList;
import java.util.List;
public class TypeInferExample {
public static void main(String[] args) {
addAges(new ArrayList<>(), 12, 12);
}
static void addAges(List<Integer> ageInYears, int firstAge, int secondAge) {
ageInYears.add(firstAge);
ageInYears.add(secondAge);
System.out.println(ageInYears);
}
}

The forEach() Method

Java 8 adds the forEach() method as a java.lang.Iterable interface, helping developers focus more on the program’s logic than the nuts and bolts of creating an Iterator and using it to traverse over a Collection. This helps improve code readability and reduces the risk of compiler errors when performing common tasks.

We can use the forEach() method as part of a class or as a lambda expression. Earlier on in this Java 8 new features tutorial, we used a shopping list example. If we use that same example, we can print the list using the forEach() method:

public class Java8Example {
public static void main(String args[]) {
List<String> shoppingList = new ArrayList<String>();
shoppingList.add("Onions");
shoppingList.add("Bread");
shoppingList.add("Flour");
shoppingList.add("Sugar");
shoppingList.add("Chicken");
// Print out each item in the shopping list usig Java 8's
// forEach
shoppingList.forEach(item -> System.out.println(item))
}
}

The Date/Time API

The java.time package includes a new date-time API as of Java 8. This API offers several useful features, including simplified timezone management and formatting. The old Date class has been deprecated, so developers are encouraged to use LocalDate and LocalTime instead. 

Time zone management is handled via OffsetDate, OffsetDateTime, and OffsetTime.

  • LocalDate: Represents dates in the format yyyy-MM-dd
  • LocalTime: Represents times in the format HH-mm-ss-ns
  • LocalDateTime: Represents both dates and times using the format yyyy-MM-dd-HH-mm-ss-ns

Using other formats, you can employ the DateTimeFormatter to display and parse date-time objects.

Concurrency in Java 8

Several major changes improved the concurrency API as of Java 8, including the newWorkStealingPool() method. It creates a work-stealing thread pool that can use the available processors at the desired parallelism level.

Other improvements to the concurrency API include:

  • New methods for ConcurrentHashMap: forEach(), forEachEntry(), forEachValue(), reduce(), merge() and search().
  • The CompleteableFuture can have its value and status explicitly set

Stream API for Bulk Data Operations

The Java 8 Stream API is used to perform bulk data operations on collections. This is found in java.util.Stream and can be used for both parallel and serial execution. The Stream API is one of the most interesting Java 8 features, especially given big data’s increasing importance. It enables easy, quick data filtering and processing in collections.

Two new methods on the collection interface are stream() and parallelStream(). These default methods work exactly as their names suggest, with stream() allowing the stream to be opened for sequential execution, while parallelStream() is used for parallel execution.

The methods can be used to open a list as follows:

Stream<Integer> sequentialStream = shoppingList.stream();
Stream<Integer> parallelStream = shoppingList.parallelStream();

The above would open the shoppingList ArrayList from our previous examples as a sequential or parallel stream. The list can then have actions, such as filter() performed on it.

Why Use Streams?

Parallel streams are designed for large data collections. Prior to Java 8 and the Stream API, working with a large collection would require a developer to write their own iteration. 

This is an issue for several reasons:

  1. Developers waste time writing external iteration rather than focusing on the application’s business logic.
  2. In most cases, the developer will end up implementing simple sequential iteration, which is unsuitable for handling large collections.
  3. Even simple iteration requires extra code, which creates technical debt, risks compiler errors, and makes the code harder to maintain.

Java 8's Stream API introduces internal iteration. It relies on Java's internal methods for iteration, filtering, and mapping. The Stream API uses functional interfaces, so it can be used with lambda expressions, making easy-to-read, concise code. 

Remember, the Stream API should use stateless lambda expressions. If your expressions are stateful, you might experience unexpected results when performing parallel processing. However, this should not be an issue with sequential streams.

Rather than developers having to reinvent the wheel and manually filter or remove duplicates, Java can comfortably handle these tasks. 

However, there are some limitations. Streams are consumable. It's not possible to create one stream and use it multiple times. However, parallel processing features allow the Stream API to offer high performance with large applications. In addition, type-specific classes IntStream, DoubleStream, and LongStream are useful when working with those primitives. 

Once a Stream is opened, several intermediate and terminal operations are available, including:

  • Filter(): to test each item in the stream against a condition, and generate an item list that matches the filter.
  • Map(): to apply functions to the stream.
  • Sorted(): to apply a sort to the stream, with the type of sort set with a comparator argument.
  • flatMap(): to create a stream from the list stream, which may be useful if it contains arrays.
  • Count(): to count the number of items in the stream.
  • forEach(): to iterate over the items in the stream and perform an action on each one.
  • findFirst(): to search through the stream, find the first item matching the provided filter, then terminate the operation.

The above isn’t a complete list of Stream API features. However, for those who work with large data collections, these new Java 8 features are worth learning.

Other Useful Java 8 Features

We’ve covered some of the most interesting Java 8 features. However, this release added several other interesting improvements, including:

Java IO Improvements

Java 8 offers several new features for opening and reading files, including:

  • Files.list(): this takes a path and returns a lazily populated stream listing the files in the directory
  • Files.lines(): reads a file and returns a stream containing all of the lines of the file
  • Files.find(): searches for a file and returns a stream lazily populated with the files path
  • BufferedReader.lines(): opens a buffered reader and returns a stream containing the lines read from the reader.

Base64 Encoding and Decoding

The new java.util.Base64 class provides encoding and decoding support for Base64 in a MIME-friendly format. The encoder will not add a line feed to the end of the output, and the decoder will reject any characters outside of the set: "A-Za-z0-9+/ ".

General Changes, Tools, and Improvements

The Java 8 release introduced new tools, added new features and removed outdated features. For example, both the JDBC-ODBC bridge and PermGen memory space have been removed.

Java 8 added the jdeps command, a command line tool and class dependency analyzer that reads .jar files and enumerates dependencies on a package or class level.

Conclusion

Java 8 is still relevant in many programmers’ day-to-day work. You can maintain your knowledge by exploring Java IDEs and courses. 

But here’s a fun fact — Java 8 is still a common job requirement for new positions in the tech world. Now that you’ve explored Java 8 features, consider continuing your preparation for your next career development. 

Java Programming Masterclass updated to Java 17

Frequently Asked Questions

1. What is Meant By Java 8?

Java 8 refers to the 8th version of the Java Platform. This was a major release with extended support and several new API features, tools, and changes to the language. Because this release was such a major milestone, it's popular with developers who want a stable platform for their applications.

2. What Are the Characteristics of Java 8?

Java 8 is a Long-Term Support (LTS) release of Java. This means unlike most other releases, which are supported with updates for just six months after release, users can expect a much longer period of standard support, with the option of premium, extended support after that. Java 8 is already more than eight years old, and extended support is available until December 2030.

As of April 2019, Java 8 SE is free for general, non-commercial computing use. Java 8 U 202 and earlier versions carry no license fee. However, those who wish to use Java 8 SE for commercial purposes must pay for updates.

3. What Are the Advantages of Java 8 Features?

Java 8's new features offer several potential benefits:

  • Easier to write human-readable code
  • More time for the developer to focus on business logic rather than re-implementing commonly used algorithms
  • Reduced risk of compiler errors
  • Stability for developers and users alike through LTS

4. What is the Latest Version of Java?

As of August 2022, the latest version of Java is Java 18, released in March 2022. Java 19 is set to replace Java 18 in September 2022. Both are non-LTS releases.

People are also reading:

By Simran Kaur Arora

Simran works at Hackr as a technical writer. The graduate in MS Computer Science from the well known CS hub, aka Silicon Valley, is also an editor of the website. She enjoys writing about any tech topic, including programming, algorithms, cloud, data science, and AI. Traveling, sketching, and gardening are the hobbies that interest her.

View all post by the author

Subscribe to our Newsletter for Articles, News, & Jobs.

Thanks for subscribing! Look out for our welcome email to verify your email and get our free newsletters.

Disclosure: Hackr.io is supported by its audience. When you purchase through links on our site, we may earn an affiliate commission.

In this article

Learn More

Please login to leave comments