From Java8 to Java11

This is a list of the changes at Java’s API I found interesting or that I may use frecuently. Not all the changes from Java9, 10 & 11 are listed here.

Java 9

Java REPL (JShell)

It stands for Java Shell. It’s used to easily execute and test any Java construction like a class, interface, enum, etc.

Module System

The way we deploy Java-Based applications using jars has a lot of limitations & drawbacks. Some of these are: The JRE & JDK are too big; JAR files are too big to use in small devices and applications; There’s no strong encapsulation, public is open to everyone.

The new Module System introduces new features to avoid all this. More information here.

Factory Methods for Inmutable Collections (List, Map, Set & Map.Entry)

(I’ll use Lists as an example in this file, but this is valid for Maps and Sets too)

Until Java8 we could use Collections.unmodifiableList() to achieve this, but this is really verbose. Now we can do the same with

List inmutableList = List.of("bla", "ble", "bli");

Private methods in Interfaces

To avoid redundant code and more re-usability we can use private and private static methods directly in interfaces now. Their behaviour is the same as in a normal class

public interface Card {

	private Long createcardID() {
		// calculate and return ID

	private static void displayCardDetails() {
		// implement


Try-with resources improvements

The new version improves the one which was implemented in Java SE 7 with better automatic resource management.

Java SE 7 Example

BufferedReader reader1 = new BufferedReader(new FileReader("file.txt"));
try(BufferedReader reader2 = reader1) {
	// do something

Java 9 Example

BufferedReader reader1 = new BufferedReader(new FileReader("file.txt"));
try(reader1) {
	// do something

Optionals’ improvements


If a value is present in the given Optional object, stream returns a sequential Stream with that value. Otherwise, it returns an empty Stream.

Stream<Optional> employee = this.getEmployee(id);
Stream employeeStream = employee  

#ifPresentOrElse(Consumer<? super Tl> action, Runnable emptyAction)

If a value is present, performs the given action with the value, otherwise performs the given empty-based action.

Optional<Integer> opt = Optional.of(4);
	() -> System.out.println("Not found"));

#or(Supplier<? extends Optional<? extends T» supplier)

Returns the value contained by the Optional if it has one, or the value given by the supplier if empty.

Optional<String> opt = Optional.of("bla");
Supplier<Optional<String>> supplier =  
	() -> Optional.of("ble");
System.out.println(opt.or(supplier)); // bla
Optional<String> opt = Optional.empty()
Supplier<Optional<String>> supplier =  
	() -> Optional.of("ble");
System.out.println(opt.or(supplier)); // ble


#takeWhile(Predicate) / #dropWhile(Predicate)

Takes a predicate as an argument and returns a Stream of the subset until the predicate returns false for the first time. If the first value is false, it gives an empty Stream back.

Stream.of(1, 2, 3, 4, 5)
	  .takeWhile(i -> i < 4)
	  .forEach(System.out::println); // 1  
					 // 2  
					 // 3  

#iterate(T seed, Predicate<? super T> hasNext, UnaryOperator next)

It’s similar to the for loop. First parameter is init value, second is the condition and third is to generate the next element.

// start value = 2; while value < 20;   
// then value *= value  
IntStream.iterate(2, x -> x < 20, x -> x * x)


Returns a sequential Stream containing a single element, or an empty Stream if null.

Stream<Integer> i = Stream.ofNullable(9);
i.forEach(System.out::println); // 9
Stream<Integer> i = Stream.ofNullable(null);
i.forEach(System.out::println); //


Java 10

Local-variable Type Inference (var)

It adds type inference to declarations of local variables with initializers. It can only be used in the following scenarios:

  • Limited only to local variable with initializer
    var numbers = List.of(1, 2, 3, 4);
  • Indexes of foreach loops
    for(var number : numbers) {
      // do something
  • Local declared in for loop
    for(var i = 0; i < numbers.size(); i++) {
      // do something

API Improvements


Returns an unmodifiable list, map or set containing the entries provided. For a List, if the original list is subsequently modified, the returned List will not reflect this modifications.

List<String> strings = new ArrayList<>();

List<String> copy = List.copyOf(strings);

System.out.println(strings); // bla, ble, bli
System.out.println(copy); // bla, ble


Different methods to collect into a unmodifiable collection.

List<String> strings = new ArrayList<>();

List<String> unmodifiableStrings =  


Is the same as Optional#get() but the doc states that this is a preferred alternative.

String name = "bla";
Optional<String> optionalName = Optional.ofNullable(name);
String s = optionalName.orElseThrow(); // bla
Optional<String> empty = Optional.empty();
// throws java.util.NoSuchElementException  
String s = empty.orElseThrow();  


Java 11

Single-file Applications

Now it’s possible to run single-file applications without the need to compile. It really simplifies the process to test new features.

In the file we have to write the following shebang

#!/usr/bin/java --source11

To run


Parameters before the name of the source file are passed as parameters to the java launcher. Parameters after are passed as parameters to the program.

java -classpath /home/bla/java Param1

Type inference for lambdas

var was introduced in Java10. The ability to use it in lambdas has been introduced in Java11.
	.map((var s) -> s.toLowerCase())

We already had type inference in lambdas, the difference is that with var we can use now type annotations to Lambda parameters.
	.map((@Notnull var s) -> s.toLowerCase())

More info on type annotations.

String API improvements


Allows concatenating a String with itself a number x of times

String s = "bla ";
String result = s.repeat(2); // bla bla


Checks wether a String is empty or contains a whitespace

String s = "";
boolean result = s.isBlank(); // true


Deletes whitespace on the start & end of a String. The difference with String#trim is that this is unicode aware. It relies on the same definition of whitespace as String#isBlank. This is a preparation for raw strings.

String s = " bla ";
String result = s.strip(); // bla (without spaces)


To easily split a String into a Stream of separate lines.

String s = "bla\nble";
List<String> lines = s.lines()  
	.collect(Collectors.toList()); // bla  
  				                 // ble