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
*/