Significant Changes in JDK 9 to 21
28 Feb 2024Major New Functionality
- Java 9 introduces a new level of abstraction above packages, formally known as the Java Platform Module System (JPMS), or “Modules” for short.
- JAXB and JAX-WS are no longer bundled with JDK 11 and higher.
- The Garbage-First Garbage Collector (G1 GC) is the default garbage collector in JDK 9 and later releases.
- The use of the ‘var’ in variable declarations makes writing code simple and error-free.
- The
switchstatement was extended so that it can be used either as a statement or an expression. This is a preview language feature.
///////////////// old way /////////////////
int number = 3;
String result;
switch (number) {
case 1:
result = "one";
break;
case 2:
result = "two";
break;
default:
result = "many";
}
System.out.println(result); // Outputs "many"
///////////////// new way /////////////////
int number = 3;
String result = switch (number) {
case 1 -> "one";
case 2 -> "two";
default -> "many";
};
System.out.println(result); // Outputs "many"Then, Pattern Matching for switch Expressions and Statements has been introduced
public class Shape {
enum Color { RED, GREEN, BLUE }
record Circle(Color color, int radius) {}
record Rectangle(Color color, int width, int height) {}
public static void main(String[] args) {
var shape = new Circle(Color.RED, 5);
var description = switch (shape) {
case Circle c && c.color() == Color.RED -> "Red circle";
case Rectangle r && r.color() == Color.BLUE -> "Blue rectangle";
case Circle c -> "Circle of color " + c.color();
case Rectangle r -> "Rectangle of color " + r.color();
default -> "Unknown shape";
};
System.out.println(description);
}
}- The
NumberFormatadded support for formatting a number in its compact form.
double number = 1000;
// Create a NumberFormat instance for the current locale
NumberFormat nf = NumberFormat.getCompactNumberInstance(new Locale("en", "US"), NumberFormat.Style.SHORT);
// Format the number
String formattedNumber = nf.format(number);
System.out.println(formattedNumber); // Outputs "1K"- Text blocks were added to Java language, which provide developers with control over the format when desired.
String html = """
<html>
<body>
<p>Hello, world!</p>
</body>
</html>
""";- The implementation used by
java.net.Socketandjava.net.ServerSocketAPIs was replaced with a simpler and more modern implementation that is easy to maintain and debug. - Adding
Records, which provides a compact syntax for declaring classes that are transparent holders for shallowly immutable data. - Pattern Matching for
instanceofthat simplifies theinstanceof-and-castidiom.
if (obj instanceof String s) {
System.out.println("The object is a String: " + s);- Sealed Classes is a Java language preview feature. Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them.
- The floating-point operations are now consistently strict, it means that the language adheres more closely to the IEEE 754 standard for floating-point arithmetic, which defines how floating-point numbers should be represented and operated on in computer systems.
public class FloatingPointComparisonExample {
private static final double EPSILON = 1E-10; // Define a small threshold value
public static boolean almostEqual(double a, double b) {
return Math.abs(a - b) <= EPSILON;
}
public static void main(String[] args) {
double x = 0.1 * 10;
double y = 1.0;
// Direct comparison using '==', which can be problematic, returns false
boolean isEqualDirect = x == y;
System.out.println("Direct comparison: " + isEqualDirect);
// Relative comparison using epsilon, returns true
boolean isAlmostEqual = almostEqual(x, y);
System.out.println("Relative comparison: " + isAlmostEqual);
}
}- UTF-8 is now the default charset for the Java SE APIs. With this change, APIs that depend on the default charset will behave consistently across all implementations, operating systems, locales, and configurations.
- Virtual threads are introduced which are lightweight threads that reduce the effort of writing, maintaining, and debugging high-throughput concurrent applications.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class VirtualThreadExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newVirtualThreadExecutor();
executor.submit(() -> {
// Simulate a long-running task
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task completed");
});
executor.shutdown();
}
}In this example, an ExecutorService is created using Executors.newVirtualThreadExecutor(), which creates a pool of virtual threads. A task is submitted to the executor, simulating a long-running operation. Because virtual threads are lightweight, this allows for a high degree of concurrency without the overhead of creating many OS-managed threads.
- introducing
Sequenced collectionswhich are collections with a defined encounter order. Each such collection has a well-defined first element, second element, and so forth, up to the last element. It is an interface that represents a sequenced collection, provides uniform APIs for accessing its first and last elements, and for processing its elements in reverse order.
Enhancements and New Concepts
Strong Encapsulation in the JDK
Some tools and libraries use reflection to access parts of the JDK that are meant for internal use only. This use of reflection negatively impacts the security and maintainability of the JDK. To aid migration, JDK 9 through JDK 16 allowed this reflection to continue, but emitted warnings about illegal reflective access. However, JDK 17 and later is strongly encapsulated, so this reflection is no longer permitted by default.
For more details, refer to: Jdeps Tool
Changes to Garbage Collection
The Garbage-First Garbage Collector (G1 GC) is the default garbage collector in JDK 9 and later releases.
A low-pause collector such as G1 GC should provide a better overall experience, for most users, than a throughput-oriented collector such as the Parallel GC, which is the JDK 8 default.
Use CLDR Locale Data by Default
Starting in JDK 9, the Unicode Consortium’s Common Locale Data Repository (CLDR) data is enabled as the default locale data, so that you can use standard locale data without any further action.
On JDK 8, if you don’t explicitly set the locale, the system’s default locale will be used. On JDK 9 and later, if you don’t specify the locale, the CLDR data will be used by default. This means that the way dates are formatted for different locales might change slightly due to updates in the CLDR data.
So, you have to define JVM system property for setting the Locale default (user.language=de, user.country=DE)
Full support for containers
The JVM now recognizes constraints set by container control groups. Both memory and CPU constraints can be used to manage Java applications directly in containers, these include:
- Adhering to memory limits set in the container
- Setting available CPUs in the container
- Setting CPU constraints in the container