LTS New Features – [ Java 21 ]

Java 21 is the LTS version after the previous Java 17.

Java 21 contains the following major new features compared to previous versions :

  • Deconstructing Records Pattern;
  • Pattern Matching for Switch;
  • Sequenced Collections;
  • Virtual Threads;
  • ProcessBuilder and Runtime.exec.

1. Deconstructing Records Pattern

Java 21 extends the existing pattern matching feature to destruct the record class instances.

In below code snippet, in the display method, it shows how to match the type of a pattern and to access its components before Java 21.

In the display2 method, it shows how to access its components directly in Java 21 by using deconstructing record pattern.

It is possible to use local variable type inference by replacing the actual component types with the var keyword like showed in the display3 method.

public class Java21NewFeaturesTest1 {

        public static void main(String[] args) {
                Person p = new Person(new Name("hello", "world"), 1);
                display(p);
                display2(p);
                display3(p);
        }

        private static void display(Object obj) {
                if (obj instanceof Person p) {
                        Name n = p.name();
                        System.out.println("first name = " + n.first());
                        System.out.println("last name = " + n.last());
                        System.out.println("age = " + p.age());
                }

                System.out.println(obj);
                System.out.println();
        }

        private static void display2(Object obj) {
                if (obj instanceof Person(Name(String first, String last), int age)) {
                        System.out.println("first name = " + first);
                        System.out.println("last name = " + last);
                        System.out.println("age = " + age);
                }

                System.out.println(obj);
                System.out.println();
        }

        private static void display3(Object obj) {
                if (obj instanceof Person(Name(var first, var last), var age)) {
                        System.out.println("first name = " + first);
                        System.out.println("last name = " + last);
                        System.out.println("age = " + age);
                }

                System.out.println(obj);
                System.out.println();
        }

        record Name(String first, String last) {

        }

        record Person(Name name, int age) {
        }
}
/**
 * Output:
        first name = hello
        last name = world
        age = 1
        Person[name=Name[first=hello, last=world], age=1]

        first name = hello
        last name = world
        age = 1
        Person[name=Name[first=hello, last=world], age=1]

        first name = hello
        last name = world
        age = 1
        Person[name=Name[first=hello, last=world], age=1]
 */

2. Pattern Matching for Switch

In Java 21, like an instanceof in an if condition, a switch case can now type check its value and creates a case scoped variable.

There is also an enhancement to handle NullPointerException by allowing a null case label.

public class Java21NewFeaturesTest2 {

        public static void main(String[] args) {
                Animal dog = new Dog();

                System.out.println("Dog goes : " + makeSound(dog));

                Animal cat = new Cat();

                System.out.println("Cat goes : " + makeSound(cat));

                System.out.println(makeSound(null));
        }

        private static interface Animal {

        }

        private static class Dog implements Animal {

        }

        private static class Cat implements Animal {

        }

        private static String makeSound(Animal animal) {
                return switch (animal) {
                        case Dog dog -> "woof";
                        case Cat cat -> "meow";
                        case null, default -> "???";
                };
        }
}
/**
 * Output:
        Dog goes : woof
        Cat goes : meow
        ???
 */

Guard clauses are a way to further refined the base condition of a case.

They are appended to the label before the colon or arrow and are separated by the when keyword.

public class Java21NewFeaturesTest3 {

        public static void main(String[] args) {
                System.out.println("It is a " + what(""));

                System.out.println("It is a " + what("hello world"));

                System.out.println("It is a " + what(100));

                System.out.println("It is a " + what(-100));

                System.out.println("It is a " + what(null));
        }

        private static String what(Object obj) {
                return switch (obj) {
                        case String s when !s.isEmpty() -> "none empty string";
                        case String s when s.isEmpty() -> "empty string";
                        case Integer i when i >= 0 -> "positive integer";
                        case Integer i when i < 0 -> "negative integer";
                        case null, default -> "oops";
                };
        }
}
/**
 * Output:
        It is a empty string
        It is a none empty string
        It is a positive integer
        It is a negative integer
        It is a oops
 */

3. Sequenced Collections

Java 21 introduces three new interfaces to the existing hierarchy of collections : sequenced collections, sequenced sets, and sequenced maps.

3.1 Sequenced Collections

A sequenced collection is a Collection whose elements have a defined encounter order.

It provides methods to add, retrieve, or remove elements at both ends of the collection, along with a method to get a reverse ordered view of the collection.

import java.util.ArrayList;
import java.util.List;

public class Java21NewFeaturesTest4 {

        public static void main(String[] args) {
                List<Integer> nums = new ArrayList<>() {
                        {
                                for (int i = 0; i < 10; i++) {
                                        add(i);
                                }
                        }
                };

                display(nums);

                display(nums.reversed());

                System.out.println(nums.getFirst());
                System.out.println(nums.getLast());

                nums.addFirst(-1);
                nums.addLast(10);

                display(nums);

                nums.removeFirst();
                nums.removeLast();

                display(nums);
        }

        private static void display(List<Integer> l) {
                int n = l.size();

                for (int i = 0; i < n; i++) {
                        System.out.print(l.get(i));

                        if (i != n - 1) {
                                System.out.print(",");
                        }
                }

                System.out.println();
        }
}
/**
 * Output:
        0,1,2,3,4,5,6,7,8,9
        9,8,7,6,5,4,3,2,1,0
        0
        9
        -1,0,1,2,3,4,5,6,7,8,9,10
        0,1,2,3,4,5,6,7,8,9
 */

3.2 Sequenced Sets

A sequenced set can be defined as a specialized Set which functions as a SequencedCollection, ensuring the absence of duplicate elements.

The SequencedSet interface extends SequencedCollection and overrides its reversed() method.

The only difference is that the return type of SequencedSet.reversed() is SequencedSet.

import java.util.LinkedHashSet;
import java.util.Set;

public class Java21NewFeaturesTest5 {

        public static void main(String[] args) {
                LinkedHashSet<Integer> nums = new LinkedHashSet<>() {
                        {
                                for (int i = 0; i < 10; i++) {
                                        add(i);
                                }
                        }
                };

                display(nums);

                display(nums.reversed());

                System.out.println(nums.getFirst());
                System.out.println(nums.getLast());

                nums.addFirst(-1);
                nums.addLast(10);

                display(nums);

                nums.removeFirst();
                nums.removeLast();

                display(nums);
        }

        private static void display(Set<Integer> s) {
                int n = s.size();

                int index = 0;

                for (Integer i : s) {
                        System.out.print(i);

                        if (index != n - 1) {
                                System.out.print(",");
                        }

                        index += 1;
                }

                System.out.println();
        }
}
/**
 * Output:
        0,1,2,3,4,5,6,7,8,9
        9,8,7,6,5,4,3,2,1,0
        0
        9
        -1,0,1,2,3,4,5,6,7,8,9,10
        0,1,2,3,4,5,6,7,8,9
 */

3.3 Sequenced Maps

A sequenced map is a Map whose entries have a defined encounter order.

The SequencedMap does not extend SequencedCollection and provides its own methods to manipulate elements at either end of the collection.

import java.util.LinkedHashMap;
import java.util.Map;

public class Java21NewFeaturesTest6 {

        public static void main(String[] args) {
                LinkedHashMap<String, String> persons = new LinkedHashMap<>() {
                        {
                                put("tom", "cat");
                                put("jerry", "mouse");
                                put("mickey", "mouse");
                                put("donald", "duck");
                        }
                };

                display(persons);

                display(persons.reversed());

                display(persons.firstEntry());
                display(persons.lastEntry());

                System.out.println();

                persons.putFirst("scrooge", "duck");
                persons.putLast("goofy", "dog");

                display(persons);

                display(persons.pollFirstEntry());
                display(persons.pollLastEntry());

                System.out.println();

                display(persons);
        }

        private static void display(Map<String, String> m) {
                for (Map.Entry<String, String> e : m.entrySet()) {
                        display(e);
                }
                System.out.println();
        }

        private static void display(Map.Entry<String, String> e) {
                System.out.println(e.getKey() + " : " + e.getValue());
        }
}
/**
 * Output:
        tom : cat
        jerry : mouse
        mickey : mouse
        donald : duck
        
        donald : duck
        mickey : mouse
        jerry : mouse
        tom : cat
        
        tom : cat
        donald : duck
        
        scrooge : duck
        tom : cat
        jerry : mouse
        mickey : mouse
        donald : duck
        goofy : dog
        
        scrooge : duck
        goofy : dog
        
        tom : cat
        jerry : mouse
        mickey : mouse
        donald : duck
 */

4. Virtual Thread

Traditionally, Java applications rely on OS-level threads, which are heavyweight entities managed by the operating system.

Each thread consumes significant memory resources, limiting scalability and imposing overhead on the system.

Java 21 introduces Virtual Threads which are lightweight and managed by the Java Virtual Machine (JVM) itself.

They are designed to be highly efficient, allowing thousands or even millions of virtual threads to be created without exhausting system resources.

4.1 Creating and Running a Virtual Thread

public class Java21NewFeaturesTest7 {

        public static void main(String[] args) {
                Thread t = Thread.startVirtualThread(() -> {
                        System.out.println("Running task in a virtual thread : " + Thread.currentThread());
                });

                try {
                        t.join();
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
        }
}
/**
 * Output:
        Running task in a virtual thread : VirtualThread[#24]/runnable@ForkJoinPool-1-worker-1
 */

4.2 CompletableFuture with Virtual Threads

import java.util.concurrent.CompletableFuture;

public class Java21NewFeaturesTest8 {

        public static void main(String[] args) {
                CompletableFuture<Void> future = CompletableFuture
                                .supplyAsync(() -> "hello world")
                                .thenApplyAsync(greeting -> greeting.toUpperCase())
                                .thenAcceptAsync(greeting -> {
                                        System.out.println(Thread.currentThread() + " : " + greeting);
                                });

                future.join();
        }
}
/**
 * Output:
        Thread[#23,ForkJoinPool.commonPool-worker-1,5,InnocuousForkJoinWorkerThreadGroup] : HELLO WORLD
 */

4.3 Virtual Thread Pool

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Java21NewFeaturesTest9 {

        public static void main(String[] args) throws InterruptedException {
                ExecutorService e = Executors.newVirtualThreadPerTaskExecutor();

                for (int i = 0; i < 10; i++) {
                        e.submit(() -> {
                                System.out.println("Running task in thread : " + Thread.currentThread());
                        });
                }

                e.shutdown();

                Thread.currentThread().join();
        }
}
/**
 * Output:
        Running task in thread : VirtualThread[#29]/runnable@ForkJoinPool-1-worker-4
        Running task in thread : VirtualThread[#34]/runnable@ForkJoinPool-1-worker-6
        Running task in thread : VirtualThread[#32]/runnable@ForkJoinPool-1-worker-2
        Running task in thread : VirtualThread[#24]/runnable@ForkJoinPool-1-worker-1
        Running task in thread : VirtualThread[#26]/runnable@ForkJoinPool-1-worker-3
        Running task in thread : VirtualThread[#36]/runnable@ForkJoinPool-1-worker-3
        Running task in thread : VirtualThread[#37]/runnable@ForkJoinPool-1-worker-1
        Running task in thread : VirtualThread[#38]/runnable@ForkJoinPool-1-worker-6
        Running task in thread : VirtualThread[#39]/runnable@ForkJoinPool-1-worker-3
        Running task in thread : VirtualThread[#35]/runnable@ForkJoinPool-1-worker-5
 */

4.4 Using ThreadFactory with Virtual Threads

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class Java21NewFeaturesTest10 {

        public static void main(String[] args) throws InterruptedException {
                ThreadFactory f = Thread.ofVirtual().name("app-", 1).factory();

                ExecutorService e = Executors.newFixedThreadPool(2, f);

                for (int i = 0; i < 10; i++) {
                        e.submit(() -> {
                                System.out.println("Running task in thread : "
                                                + Thread.currentThread());
                        });
                }

                e.shutdown();

                Thread.currentThread().join();
        }
}
/* Output:
        Running task in thread : VirtualThread[#26,app-2]/runnable@ForkJoinPool-1-worker-2
        Running task in thread : VirtualThread[#24,app-1]/runnable@ForkJoinPool-1-worker-1
        Running task in thread : VirtualThread[#24,app-1]/runnable@ForkJoinPool-1-worker-1
        Running task in thread : VirtualThread[#24,app-1]/runnable@ForkJoinPool-1-worker-1
        Running task in thread : VirtualThread[#26,app-2]/runnable@ForkJoinPool-1-worker-2
        Running task in thread : VirtualThread[#24,app-1]/runnable@ForkJoinPool-1-worker-1
        Running task in thread : VirtualThread[#24,app-1]/runnable@ForkJoinPool-1-worker-1
        Running task in thread : VirtualThread[#26,app-2]/runnable@ForkJoinPool-1-worker-2
        Running task in thread : VirtualThread[#24,app-1]/runnable@ForkJoinPool-1-worker-1
        Running task in thread : VirtualThread[#26,app-2]/runnable@ForkJoinPool-1-worker-2
 */

4.5 Performance Comparison between Threads and Virtual Threads

import java.time.Duration;
import java.time.Instant;

public class Java21NewFeaturesTest11 {

        public static void main(String[] args) {
                int n = 1000;

                Instant start = Instant.now();

                runThread(n);

                System.out.println("Run " + n + " threads, elapsed time = "
                                + Duration.between(start, Instant.now()).toMillis());

                start = Instant.now();

                runVirtualThread(n);

                System.out.println("Run " + n + " virtual threads, elapsed time = "
                                + Duration.between(start, Instant.now()).toMillis());
        }

        private static void runVirtualThread(int n) {
                for (int i = 0; i < n; i++) {
                        Thread t = Thread.startVirtualThread(() -> {

                        });

                        try {
                                t.join();
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                }
        }

        private static void runThread(int n) {
                for (int i = 0; i < n; i++) {
                        Thread t = new Thread(() -> {

                        });

                        try {
                                t.start();
                                t.join();
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                }
        }
}
/**
 * Output:
        Run 1000 threads, elapsed time = 458
        Run 1000 virtual threads, elapsed time = 157
 */

5. ProcessBuilder and Runtime.exec

The ProcessBuilder class provides methods for creating and configuring operating system processes.

Under the hood, Runtime.exec() uses a ProcessBuilder to start the native process.

Each ProcessBuilder instance manages a collection of process attributes.

It allows us to start a process with those given attributes.

Below are the most important methods in the ProcessBuilder class :

  • ProcessBuilder(String… command) : create a new process builder with the specified operating system program and arguments;
  • command(String… command) : set the operating system program and arguments of the process builder;
  • command() : return the operating system program and arguments of the process builder;
  • directory(File directory) : override the default working directory of the current process, by default, the current working directory is set to the value returned by the user.dir system property;
  • environment() : get the current environment variables, it returns a copy of the current process environment using System.getenv() as a Map;
  • inheritIO() : set the source and destination of the subprocess standard I/O to be the same as that of the current java process;
  • redirectInput(File file), redirectOutput(File file), redirectError(File file) : redirect the standard input, output and error destination to a file;
  • start() : start a new process with what have been configured.

The process builder class is NOT synchronized.

If we have multiple threads accessing a ProcessBuilder instance concurrently then the synchronization must be managed externally.

5.1 Execute a shell command from java code

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Java21NewFeaturesTest12 {

        public static void main(String[] args) throws IOException, InterruptedException {
                List<String> command = new ArrayList<String>() {
                        {
                                add("ls");
                                add("-l");
                                add("/tmp");
                        }
                };

                ProcessBuilder builder = new ProcessBuilder(command);

                System.out.println("command to be executed : " + builder.command());

                Process process = builder.start();

                process.waitFor();

                showExecutedResult(process);
        }

        private static void showExecutedResult(Process process) throws IOException {
                try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                        String line = null;

                        while ((line = br.readLine()) != null) {
                                System.out.println(line);
                        }
                }
        }
}
/**
 * Output:
        command to be executed : [ls, -l, /tmp]
        total 68
        drwx------ 2 ovo  ovo  4096 Mar 11 08:28 com.google.Chrome.CSHgGi
        drwxr-xr-x 2 ovo  ovo  4096 Mar 11 08:56 hsperfdata_ovo
        drwx------ 2 ovo  ovo  4096 Mar 11 08:56 mcp-T3iinl
        drwx------ 3 root root 4096 Mar 11 08:25 snap-private-tmp
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-bluetooth.service-yNoiYE
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-colord.service-IYVnUw
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-ModemManager.service-I4W7xt
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-polkit.service-Uf4auL
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-power-profiles-daemon.service-oUQVvO
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-switcheroo-control.service-ewcfhp
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-logind.service-4m6KFb
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-oomd.service-VPurMT
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-resolved.service-RAxU5c
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-timesyncd.service-WpoytK
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-upower.service-CzQ3o8
        drwxrwxrwt 2 root root 4096 Mar 11 08:25 VMwareDnD
        drwx------ 2 root root 4096 Mar 11 08:25 vmware-root_836-2722107930
 */

5.2 Inherit IO to the current JVM process

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Java21NewFeaturesTest13 {

        public static void main(String[] args) throws IOException, InterruptedException {
                List<String> command = new ArrayList<String>() {
                        {
                                add("ls");
                                add("-l");
                                add("/tmp");
                        }
                };

                ProcessBuilder builder = new ProcessBuilder(command);

                System.out.println("command to be executed : " + builder.command());

                builder.inheritIO();

                Process process = builder.start();

                process.waitFor();
        }
}
/**
 * Output:
        command to be executed : [ls, -l, /tmp]
        total 76
        drwx------ 2 ovo  ovo  4096 Mar 11 08:28 com.google.Chrome.CSHgGi
        drwxr-xr-x 2 ovo  ovo  4096 Mar 11 08:58 hsperfdata_ovo
        drwx------ 2 ovo  ovo  4096 Mar 11 08:56 mcp-T3iinl
        drwx------ 2 ovo  ovo  4096 Mar 11 08:57 pyright-8680-YUXr0FWp9OTb
        drwxrwxr-x 3 ovo  ovo  4096 Mar 11 08:57 python-languageserver-cancellation
        drwx------ 3 root root 4096 Mar 11 08:25 snap-private-tmp
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-bluetooth.service-yNoiYE
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-colord.service-IYVnUw
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-ModemManager.service-I4W7xt
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-polkit.service-Uf4auL
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-power-profiles-daemon.service-oUQVvO
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-switcheroo-control.service-ewcfhp
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-logind.service-4m6KFb
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-oomd.service-VPurMT
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-resolved.service-RAxU5c
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-timesyncd.service-WpoytK
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-upower.service-CzQ3o8
        drwxrwxrwt 2 root root 4096 Mar 11 08:25 VMwareDnD
        drwx------ 2 root root 4096 Mar 11 08:25 vmware-root_836-2722107930
 */

5.3 Setup a custom key value map for the environment

import java.io.IOException;
import java.util.Map;

public class Java21NewFeaturesTest14 {

        public static void main(String[] args) throws IOException, InterruptedException {
                ProcessBuilder builder = new ProcessBuilder();

                Map<String, String> environment = builder.environment();

                environment.put("greeting", "hello world");

                builder.command("/bin/bash", "-c", "echo $greeting");

                builder.inheritIO();

                Process process = builder.start();

                process.waitFor();
        }
}
/**
 * Output:
        hello world
 */

5.4 Change the working directory of where the shell command is running

import java.io.File;
import java.io.IOException;

public class Java21NewFeaturesTest15 {

        public static void main(String[] args) throws IOException, InterruptedException {
                ProcessBuilder builder = new ProcessBuilder();

                builder.command("pwd");

                builder.inheritIO();

                Process process = builder.start();

                process.waitFor();

                builder.directory(new File("/tmp"));

                process = builder.start();

                process.waitFor();
        }
}
/**
 * Output:
        /home/ovo/github/BlogTests
        /tmp
 */

5.5 Redirect IO to custom replacements

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class Java21NewFeaturesTest16 {

        public static void main(String[] args) throws IOException, InterruptedException {
                ProcessBuilder builder = new ProcessBuilder();

                builder.command("ls", "-l", "/tmp");

                Path temp = Files.createTempFile("hello", "world");

                builder.redirectOutput(temp.toFile());

                Process process = builder.start();

                process.waitFor();

                try {
                        String output = new String(Files.readAllBytes(temp));

                        System.out.println(output);
                } finally {
                        Files.deleteIfExists(temp);
                }
        }
}
/**
 * Output:
        total 80
        drwx------ 2 ovo  ovo  4096 Mar 11 08:28 com.google.Chrome.CSHgGi
        -rw------- 1 ovo  ovo     0 Mar 11 09:03 hello16623931951890733813world
        drwxr-xr-x 2 ovo  ovo  4096 Mar 11 09:03 hsperfdata_ovo
        drwx------ 2 ovo  ovo  4096 Mar 11 08:56 mcp-T3iinl
        drwx------ 2 ovo  ovo  4096 Mar 11 08:57 pyright-8680-YUXr0FWp9OTb
        drwxrwxr-x 3 ovo  ovo  4096 Mar 11 08:57 python-languageserver-cancellation
        drwx------ 4 root root 4096 Mar 11 09:00 snap-private-tmp
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-bluetooth.service-yNoiYE
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-colord.service-IYVnUw
        drwx------ 3 root root 4096 Mar 11 09:00 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-fwupd.service-8Ih71S
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-ModemManager.service-I4W7xt
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-polkit.service-Uf4auL
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-power-profiles-daemon.service-oUQVvO
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-switcheroo-control.service-ewcfhp
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-logind.service-4m6KFb
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-oomd.service-VPurMT
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-resolved.service-RAxU5c
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-timesyncd.service-WpoytK
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-upower.service-CzQ3o8
        drwxrwxrwt 2 root root 4096 Mar 11 08:25 VMwareDnD
        drwx------ 2 root root 4096 Mar 11 08:25 vmware-root_836-2722107930
 */

5.6 ProcessBuilder pipeline

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Java21NewFeaturesTest17 {

        public static void main(String[] args) throws IOException, InterruptedException {
                // ls | wc -l
                ProcessBuilder ls = new ProcessBuilder("ls");
                ProcessBuilder wc = new ProcessBuilder("wc", "-l");

                List<ProcessBuilder> builders = new ArrayList<ProcessBuilder>() {
                        {
                                add(ls);
                                add(wc);
                        }
                };

                List<Process> processes = ProcessBuilder.startPipeline(builders);

                Process process = processes.getLast();

                process.waitFor();

                showExecutedResult(process);
        }

        private static void showExecutedResult(Process process) throws IOException {
                try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                        String line = null;

                        while ((line = br.readLine()) != null) {
                                System.out.println(line);
                        }
                }
        }
}
/* 
 * Output:
 *        28
 */

5.7 Runtime.exec

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Java21NewFeaturesTest18 {

        public static void main(String[] args) throws IOException, InterruptedException {
                String[] command = { "ls", "-l", "/tmp" };

                Process process = Runtime.getRuntime().exec(command);

                process.waitFor();

                showExecutedResult(process);
        }

        private static void showExecutedResult(Process process) throws IOException {
                try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                        String line = null;

                        while ((line = br.readLine()) != null) {
                                System.out.println(line);
                        }
                }
        }
}
/**
 * Output:
        total 76
        drwx------ 2 ovo  ovo  4096 Mar 11 08:28 com.google.Chrome.CSHgGi
        drwxr-xr-x 2 ovo  ovo  4096 Mar 11 09:05 hsperfdata_ovo
        drwx------ 2 ovo  ovo  4096 Mar 11 08:56 mcp-T3iinl
        drwx------ 2 ovo  ovo  4096 Mar 11 08:57 pyright-8680-YUXr0FWp9OTb
        drwxrwxr-x 3 ovo  ovo  4096 Mar 11 08:57 python-languageserver-cancellation
        drwx------ 4 root root 4096 Mar 11 09:00 snap-private-tmp
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-bluetooth.service-yNoiYE
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-colord.service-IYVnUw
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-ModemManager.service-I4W7xt
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-polkit.service-Uf4auL
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-power-profiles-daemon.service-oUQVvO
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-switcheroo-control.service-ewcfhp
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-logind.service-4m6KFb
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-oomd.service-VPurMT
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-resolved.service-RAxU5c
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-systemd-timesyncd.service-WpoytK
        drwx------ 3 root root 4096 Mar 11 08:25 systemd-private-f8faf0446766409f9d71d8a8c84a23a7-upower.service-CzQ3o8
        drwxrwxrwt 2 root root 4096 Mar 11 08:25 VMwareDnD
        drwx------ 2 root root 4096 Mar 11 08:25 vmware-root_836-2722107930
 */