Wednesday, September 27, 2023

Quiz yourself: Serializing a primitive with ObjectOutputStream

Quiz Yourself, Oracle Java Certification, Java Guides, Java Learning, Java Certification Preparation, Java Preparation, Java Prep Exam

Primitives? Objects? What should you do?


You need to serialize a long primitive value, and you have been given the following code:

long l = 5L;
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
  ... //
}

Which statement is true? Choose one.

A. The code must box the primitive using oos.writeObject(Long.valueOf(l)).
B. The primitive must be included in the outer Java class as an instance variable to be serialized.
C. The serialization should be implemented as oos.writeObject(l).
D. The serialization should be implemented as oos.writeLong(l).
E. The serialization must delegate to the DataOutput class for writing primitives.

Answer. Java serialization provides a way to represent a graph of objects as a byte sequence. This mechanism also supports primitive data directly, as part of objects. Primitive wrapper classes also implement the Serializable interface and can be serialized too. Commonly, the byte sequence that results from serialization is written to disk for long-term storage or is transmitted across a network.

In most cases, you will use the class ObjectOutputStream to create the serialized byte stream. This class provides the writeObject method proposed in option C, and that method readily handles serializing an entire graph of objects. The result can be transparently restored using the ObjectInputStream class. Note, however, that the argument to the writeObject method is an object, not a primitive.

The previous discussion tells you something about option C: If it’s used, autoboxing would occur and you would not serialize the primitive value. Instead, you would have a serialized representation of the wrapper that was created. At this point in analyzing the exam question, it’s probably too early to reject this option, but you must decide whether any other answer is a better match with the proposition of the question—which does, after all, specifically mention that you are serializing a primitive value.

It turns out that ObjectOutputStream implements the java.io.DataOutput interface. That interface defines writeXXX methods for all primitives. As a side note, DataOutput is also implemented by the class java.io.DataOutputStream, but although that class works with primitives and strings, it cannot perform serialization. This capability is described in option D, and it will allow you to use the oos object to serialize the primitive directly. From this you can determine that option D is correct. It’s definitely better than option C, and you can now confidently reject option C as incorrect.

Although you should feel confident in the correctness of option D, you should still verify that the other options are incorrect. In an exam, you might discover another option that seems correct, which would cause you to revisit your reasoning. But if you’re tight on time, you might go with the first answer that appears correct.

Option A is incorrect: The ObjectOutputStream is able to serialize primitives directly, which means that boxing is not necessary. Of course, if your application calls for it, you can box or autobox the primitive, but it’s not necessary. Further, the result will take substantially more space in the resulting byte sequence.

Option B is also incorrect for essentially for the same reason as with options A and C. Certainly primitives that are members of objects are handled correctly, but it’s not necessary to wrap a primitive in such an object just to get it out into the byte sequence.

Option E is incorrect, because, as discussed earlier, java.io.DataOutput is an interface, not a class, as is suggested in the stem. The interface declares the signatures of various methods, including the writeLong method you will use, but writeLong is (and, in fact, all the methods of this interface are) abstract. The implementation is provided by the java.io.ObjectOutputStream class; therefore, delegation in the code is not necessary or possible.

Conclusion. The correct answer is option D.

Source: oracle.com

Friday, September 15, 2023

Quiz yourself: Deserializing objects with readObject

Quiz Yourself, Oracle Java Career, Oracle Java Skill, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation, Oracle Java Object

You should know how the readObject method works—and when it won’t compile.

Given the following Person record

record Person(String name) implements Serializable {}

and the following method fragment

try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename))) {
    ... // code here
    System.out.println(person.name());
}

Assume that the try block is completed with necessary catch blocks and the file contains a serialized Person and opens correctly.

The code from which option, when placed where the comment is, properly deserializes the object and allows the rest of the code to work? Choose one.

A. var person = (Person) null;
if (in.readObject() instanceof Person p) {
  person = p;
}
B. var person = in.readObject();

C. Person person = null;
if (in.readObject() instanceof Person) {
  person = in.readObject();
}

D. var person = null;
Object o = in.readObject();
if (o instanceof Person) {
  person = (Person) o;
}

Answer. We will skip over option A for the moment.

Option B would read the serialized object from the file, but the return type of the readObject method is declared as Object; therefore, var will infer the type of the person variable to be Object too. This means that person.name() will not compile. From this you can see that option B is incorrect.

Option C is incorrect for two reasons. The semantics of the code would cause the input stream to be read twice, and the result of the first reading operation would be lost. However, there is a more severe problem; as already mentioned, the return type of readObject is Object, and the following assignment to the person variable (which is declared explicitly as being of type Person in this option) would fail unless an explicit cast were added:

person = in.readObject(); // assignment fails!

The following cast would fix the problem:

person = (Person)in.readObject(); // this could work

Option D is also incorrect. When var is used to declare a local variable (other than a lambda formal parameter), the compiler must be able to decide, unambiguously, the intended type of the variable based on the expression that is assigned for initialization. The null value does not provide type information—it is assignment-compatible with any reference type. So, compilation would fail.

Changing the declaration to the following would allow option D to work:

var person = (Person) null;

Option A is correct. It uses the newer “pattern matching for instanceof” feature that was added (after several previews) in Java 16 as JEP 394. The effect is that if the deserialized object is, in fact, assignment-compatible with the Person type, the variable p is declared and initialized with the reference to that object. The scope of p is such that it’s usable only in the parts of the code where it has definitely been initialized. For example, if there were an else clause on the if statement, p would be out of scope in that else clause.

Conclusion. The correct answer is option A.

Source: oracle.com

Monday, September 11, 2023

Java Card 3.1: Security Services

Java Card Technology, Core Java, Oracle Java Certification, Java Prep, Java Preparation, Oracle Java Tutorial and Materials, Oracle Java Guides

A key goal of version 3.1 is to ensure the availability of security services on a large range of secure hardware, including smartcards, embedded chips, secure enclaves within microprocessor units (MPUs) and microcontroller units (MCUs), and removable SIMs. It was designed to support the growth of existing Java Card markets, such as payment, identity, and connectivity markets, while enabling new IoT use cases with dedicated features. This blog entry covers those features.

Security services in Java Card 3.1 include the Certificate API, the Key Derivation API, the Monotonic Counter API, and the System Time API. Let’s look at these in more detail.

Certificate API. Cryptographic certificates are critical for security and serve as a basis in a public key infrastructure (PKI) to establish trust between different entities. A notable example of a protocol using cryptographic certificates is the Transport Layer Security (TLS) protocol. Based on certificate chains, a client (such as an IoT gateway) and a server (for example, an IoT cloud service) can authenticate each other.

The javacardx.security.cert package is an efficient way to manage cryptographic certificates such as X.509 certificates for memory- and resource-constrained devices.

With Java Card’s Certificate API, it is possible to verify a certificate signature, select and check some of its fields and extensions, and access its public key—without needing to create a dedicated certificate object that is potentially useless in the future. You can also build a certificate object (for example, for root certificates) that will be reused later, while deciding on fields and extensions that need to be associated with this certificate object and storing only useful components of the certificate.

With these mechanisms, an application has an efficient way to verify a certificate chain, check sensitive certificate fields, and keep track of trusted public keys.

Key Derivation API. Pseudorandom functions (PRFs) and key derivation functions (KDFs) are widely used in cryptography to derive sensitive data such as a secret key. They make it possible to stretch a secret or to derive multiple keys from it.

A typical usage is the derivation of a password to store a derived value without needing to store the initial password value. Another common example is the derivation of the shared secret established by a key-agreement operation in Diffie-Hellman (DH) or Elliptic-Curve Diffie Hellman (ECDH) key exchange. Another example is the TLS handshake protocol, which uses a PRF applied to a shared secret in between a client and a server to generate the cryptographic block material used during a TLS session between the two peers.

Java Card 3.1 introduces the javacardx.security.derive package. Its class DerivationFunction permits the management of both PRF and KDF algorithms, and it is easily extensible. Currently, eight algorithms are proposed that enable support for the International Civil Aviation Organization (ICAO) or TLS protocols, among others.

In addition, the Key Derivation API guarantees both the security of the derivation keys and the derived keys by encapsulating them into trusted objects.

Monotonic Counter API. To prevent replay attacks, numerous security protocols use monotonic counters, which are counters whose value can only increase. Once the value of a monotonic counter has been used, the counter is incremented (typically by 1). Thus, if a counter value has been attached to a given protocol payload at a certain time, it is guaranteed that the same counter value cannot be reused and attached to the same protocol payload later. This allows a third party consuming the protocol payload to know whether it has already been used by checking the counter value.

Device attestation (also known as remote attestation) is an example of a payload that needs to be protected against replay attacks. A remote attestation is a signature of software measurements running on a given device, and the attestation is sent to a third party. The third party can check whether a device is running unaltered software and can make sure the attestation is current and not replayed from the past.

External secure storage is another example requiring a monotonic counter. DRM licenses or the number of PIN entry tries are typical sensitive data protected by a Java Card secure element. In some hardware architectures, like the ones with an integrated secure element, such sensitive data might be stored in the memory flash of the host device; that is, in storage external to the secure element hardware itself. Thus, untrusted and rogue applications from the device host might be able to save and restore later such sensitive data. In the case of a DRM license, this risk would mean that access to a content item is granted an unlimited number of times instead of the initial limited times granted originally. Hence, in addition to integrity and confidentiality, the secure element must guarantee an anti-replay protection to sensitive data stored in external secure storage.

The javacardx.security.util package and its MonotonicCounter class enable the creation and management of multiple monotonic counters of up to 64 bits each. The Monotonic Counter API guarantees the atomicity of the update of the counter value.

System Time API. Time stamping and time interval calculation are important security operations. Time stamps enable you to record or check the time at which an event occurred. Estimating a time interval allows you to limit the duration of a transaction, for instance.

Java Card 3.1 introduces the package javacardx.framework.time, which has two classes:

◉ SysTime serves to retrieve the uptime; that is, the time elapsed since system boot. It does not require an internal clock.
◉ TimeDuration represents a time duration with microsecond resolution. Several operations, such as comparisons and conversions as well as plus and minus operations, are supported.

Java Card’s System Time API can support a variety of use cases related to device security. For example, consider an IoT device managing a temperature sensor in the chemical industry. This monitoring system is critical and needs to react to unexpected temperature variations. With the new I/O mechanism introduced in Java Card 3.1 and the System Time API, an application has the ability to retrieve a temperature value securely and to assess the elapsed time since the beginning of the measurement. If the time is too short or too long compared with the average expected value, this is reported to the monitoring system, which will trigger corrective operations such as changing the sensor or checking for a corruption of the IoT device software.

Source: oracle.com

Friday, September 8, 2023

Quiz yourself: Collectors, comparators, and type inferencing in Java

Quiz Yourself, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation, Oracle Java Preparation


Your colleague is working on an application that must find a most-frequently used word in a Stream<String>, and each element of that stream is a single word. The stream is provided by the reference strm.

Which of these pipeline expressions can perform this task? Choose two.

A.

strm.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting())).entrySet().stream().sorted((e1,
e2)-> e2.getValue().compareTo(e1.getValue())).findFirst()

B.

strm.collect(Collectors.groupingBy((String s) -> s, 
Collectors.counting())).entrySet().stream().sorted(
Map.Entry.comparingByValue()).findFirst()

C.

strm.collect(Collectors.groupingBy(a -> a,
Collectors.counting())).entrySet().stream().sorted(
Map.Entry::comparingByValue).findFirst()

D. 

strm.collect(Collectors.groupingBy(Function.identity(), 
Collectors.counting())).entrySet().stream().sorted((e1, 
e2)-> e2.getValue() - e1.getValue()).findFirst()

E.

strm.collect(Collectors.groupingBy(___ -> ___, 
Collectors.counting())).entrySet().stream().max(Comparator.
comparing(e -> e.getValue()))

Answer. This question investigates aspects of the Collectors utilities, ordering using a Comparator, type conversion rules, and type inferencing in Java’s generics system.

All but one of the question’s code fragments take the following four-step approach to the problem, with variations in the implementation details:

Step 1. Take each word and use the groupingBy collector to build a map in which each key is a word from the stream, and the value accumulates the number of occurrences of the word in the stream. The groupingBy method is a factory that builds a collector, and in the form used here it takes two arguments. The first derives a key for the resulting map from the object in the stream. In this case, the object in the stream is the word you want to use as the key, so no change is necessary. The second argument is Collectors.counting(), which is used as a downstream collector that modifies the value stored against the key in the map to be a count of the number of times the key has been seen, instead of being a list of the objects that produced that key.

Step 2. The code then extracts a stream from that map. The Map interface cannot directly provide a Stream, but it provides access to a Set<Map.Entry> using the entrySet method. A Map.Entry<K, V> is a single key-value pair (a tuple of K and V, essentially, although Java does not provide tuples at a language syntax level). A Set does allow drawing a Stream directly, which is the next step.

Step 3. Sort the stream of entries. This must be done in descending order of the value part of the entry since that’s the count of occurrences of the word represented in the key.

Step 4. The reason that descending order is essential is that the final step is to pull the first entry object from the resulting stream using a findFirst method. If the stream were sorted in ascending order, you’d get the least-used word rather than the most-used one. It’s worth noting that if two or more words tie for top place in usage, you’ll get one of them with no control over which one you get. But you can ignore that possibility in this question; notice the specification says, “a most-frequently used word,” rather than “the most-frequently used word.” Therefore, you don’t need to worry about a tie.

Option A is a correct implementation of this approach. The first argument to the groupingBy factory method should be a function that takes the stream object and returns the key. As noted, in this situation the key is the same as the stream object, and Function.identity() is a factory for a Function object that returns its own argument unchanged. The second argument is the counting factory, and that’s exactly what was described above. Next, the sorting is performed using Comparator explicitly coded as a lambda expression. The effect is to compare the value of the two entry objects, but because the code is written as e2 … compareTo … e1, the ordering will be reversed and, therefore, descending.

Option B is incorrect. Although it is syntactically valid, the stream is sorted in ascending order; therefore, it will return an entry with a least-frequently used word. You could correct this code by reversing the ordering of the comparator, as follows:

strm.collect(Collectors.groupingBy((String s) -> s, 
Collectors.counting())).entrySet().stream().sorted(Map.Entry.<String, 
Long>comparingByValue().reversed()).findAny();

Notice the use of <String, Long> before comparingByValue().reversed(). The code fails to compile without this because the Java compiler cannot infer the generic type parameters in this situation. Therefore, this syntax is used to specify the types explicitly.

Another difference in the approach taken by option B, which is valid in isolation, is the replacement of Function.identity() with an explicit lambda: (String s) -> s. This is valid, and in this code has the same effect of returning its argument unchanged. The argument type is redundant, but it is valid because the objects in the stream are of String type.

Option C also changes Function.identity() to an explicit lambda: a -> a. As with option B, this has the same effect and is not a problem.

Looking at the second argument to groupingBy—the downstream collector—you know this must be an object of Collector type. However, Map.Entry::comparingByValue is a method reference; if the code were compilable in this location, it would create an object equivalent to a lambda expression such as x -> Map.Entry.comparingByValue(x). That would be a Function that returns a Comparator. However, Function is not valid at this point, because the code needs an actual Comparator object. From this you can see that option C is incorrect.

Option D is also incorrect. As already mentioned, Collectors.counting() returns a collector that counts the number of occurrences of elements. The total is accumulated as a Long. Later in the hand-coded comparator, Long is subtracted from Long, and this gives a Long result (which could, under appropriate conditions, be autounboxed to a long primitive). However, the return type of the comparator’s compare method must be int, and it’s not legal to convert a Long (or even a long) to an int without an explicit cast. The following code includes the necessary cast and would work correctly:

(e1, e2)-> (int)(e2.getValue() - e1.getValue())

Option E changes two things. First, it uses an explicit lambda instead of Function.identity(). You saw this in two previous examples, but here the variable name is odd. It’s ___ (which is a triple underscore) rather than something more normal such as s or a. It turns out that this is actually a valid identifier in Java. A single underscore is a reserved keyword (since Java 9), but a leading underscore followed by more characters (even if they’re just more underscores) or an underscore embedded in the middle of a name is legal. It’s worth noting that using a triple underscore as a variable name would likely get some raised eyebrows in a code review, so don’t tell anyone we said it’s a good idea. We definitely are not suggesting that it’s anything other than a curiosity!

Another difference in this example is the use of the max terminal operation rather than ordering followed by findFirst. This works correctly and might be more efficient because it doesn’t require the processing of the second stream to build a structure containing references to all the elements; instead, only a reference to the largest element so far needs to be kept. In any case, option E is correct.

Here are three interesting side notes.

First: You can read about the difference between Function.identity() and a -> a in our earlier quiz “Quiz yourself: Mixing and matching Java primitives with generics in a stream.”

Second: In the case of English, the most-frequently used word is generally “the,” but in any given body of text, that might not be the case. Of course, this quiz’s code could be used to analyze text in any language.

Third: The stem of the question mentions that the items listed are expressions. As such they’re not complete Java code but must be used in some larger context, perhaps to provide a value to be assigned to a variable or as an argument to a method invocation. However, a general guide for the exam is that if code is valid as shown but incomplete, you should assume there’s enough supporting code around it to allow it to work if it can do so; you’re being asked solely about the validity of what’s shown, not about what you can’t see. The Java SE 17 Developer exam (1Z0-829) has a section called “Assume the following” under the expandable tab “Review exam topics.” Among other things, it specifies the following:

If sample code does not include package or import statements, and the question does not explicitly refer to these missing statements, then assume that all sample code is in the same package, or import statements exist to support them.

Conclusion. The correct answers are options A and E.

Source: oracle.com

Thursday, August 31, 2023

Quiz yourself: Try-with-resources and PreparedStatement database access

Quiz Yourself, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Certification, Oracle Java Guides, Oracle Java Learning

The best uses—and some limitations—of try-with-resources


Imagine that your system runs against a JDBC driver and a target database that fully and correctly implement the JDBC specifications, and your system has code that handles database resources properly by using the following try-with-resource statement:

String url = … //
var sql = "SELECT * FROM EMPLOYEE";
try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql);
     ResultSet rs = ps.executeQuery()) {
  while (rs.next()) {
    … //
  }
}

You want to refine the selection so it returns not all rows but only rows that match a certain criteria. Therefore, you changed the SQL code to the following:

var sql = "SELECT * FROM EMPLOYEE WHERE NAME LIKE ?";

Which of the following will work correctly with the new SELECT statement while ensuring that the database resources are handled properly? Choose one.


A.

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql);
     ps.setString(1, "E%");
     ResultSet rs = ps.executeQuery()) {
  while (rs.next()) {
    … //
  }
}

B.

Connection conn = DriverManager.getConnection(url);
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "E%");
ResultSet rs = ps.executeQuery();
try (conn;ps;rs) {
  while (rs.next()) {
    … //
  }
}

C.

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql)) {
  ps.setString(1, "E%");
  ResultSet rs = ps.executeQuery();
  while (rs.next()) {
    … //
  }
}

D.

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql)) {
  ps.setString(1, "E%");
}
try (ResultSet rs = ps.executeQuery()) {
  while (rs.next()) {
    … //
  }
}

Answer. This question investigates the use of the try-with-resources construction and also uses a PreparedStatement.

First, look at the usage of the PreparedStatement. You have modified the SQL statement to the following form:

"SELECT * FROM EMPLOYEE WHERE NAME LIKE ?"

The question mark at the end is a placeholder for a value and, before the statement is executed, a value must be provided for this. The value can be supplied correctly by the method call ps.setString(1, "E%"), which is present in all the answers’ proposed code.

Two further issues remain. One is whether the structure of the code is valid. Another is whether the rather vague requirement, “ensuring that the database resources are handled properly,” is satisfied. This latter stipulation means that all the database resources must be closed reliably by the end of the code.

The try-with-resources structure was added in Java 7 to simplify the reliable closing of resources, which can be a bit messy using only the finally block provided before then. It closes all the resources that are declared inside the parentheses that immediately follow the keyword try. As a side note, the resources are closed in the opposite order in which they are listed inside the parentheses. However, there is a restriction that code placed inside the parentheses must be one of two types of elements, as follows:

◉ An initialized declaration of a final or effectively final variable of a type that implements the interface AutoCloseable.
◉ A simple reference to a final or effectively final variable of a type that implements the interface AutoCloseable that was initialized before the start of the try-with-resources structure. (This syntax feature was added in Java 9.)

From the description above, you can immediately determine that option A presents invalid syntax because it places the call to setString inside the resources block; therefore, option A is incorrect.

In option B, you can see that the syntax is correct. It uses the second syntax option described above; it simply names effectively final resources that were declared before entry into the try block. However, although the code will compile and run, it’s not a safe approach for closure of the resources. If an exception were to arise after some of the resources have been created but before entry to the try structure, no attempt would be made to close the resources. Because of this, option B is incorrect.

Option D is also incorrect. The code deals with three resources: a Connection, a PreparedStatement, and a ResultSet. There are two separate try-with-resource statements: one for the Connection and PreparedStatement and another for the ResultSet. There are two problems with this; one is syntactic, and one is semantic. The syntax problem is that resource variables declared and initialized inside the parentheses of a try-with-resources structure behave as formal parameters. Such variables have a scope that ends with the closing curly brace of the pair that immediately follows the closing parenthesis of the try structure. This is the same as with method formal parameters. Because of this, the ps variable is out of scope before the second try-with-resource begins with the following line:

try (ResultSet rs = ps.executeQuery()) {

Additionally, even if the scope problem didn’t cause the code to fail to compile, it’s semantically incorrect. The issue is that the PreparedStatement ps and the Connection conn that supports it would have been closed at the end of the first try-with-resources and would not work for use in the second try-with-resources.

What about option C? The syntax is valid, and the code properly handles all opened resources. That might seem surprising because the ResultSet is declared in the body of the try block instead of in the resource list inside the parentheses. Because of this, the resource is not automatically closed. However, this is allowed. The documentation for Statement—which is a parent interface of PreparedStatement—explicitly says

Note: When a Statement object is closed, its current ResultSet object, if one exists, is also closed.

Because the PreparedStatement is declared inside the parentheses, it will be automatically closed, and that guarantees that the ResultSet will be closed reliably, too. Therefore, option C is correct.

A couple of side notes are worth raising.

First, what’s that waffle in the opening of this question about “fully and correctly implement the JDBC specifications”? It turns out that not all databases and drivers implement the specification fully and correctly; some actually keep a ResultSet opened after closure of their Statement. Therefore, in practice option C might let you down. A more robust approach would be to use two try-with-resource blocks nested, as follows:

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql)) {
  ps.setString(1, "E%");
  try (ResultSet rs = ps.executeQuery()) {
    while (rs.next()) {
      … //
    }
  }
}

The second side note is that the items in the resource list inside the parentheses of try-with-resources must be separated using semicolons, but also permitted (but not required) is a trailing semicolon. This is similar to the array literal syntax that allows a trailing comma. Such a feature might seem odd, but it can simplify reordering the elements (since the semicolons just move around with their statements and nothing will be missing). It can also simplify machine-generated code (because the machine can simply tack a semicolon on the end of each resource without having to decide if it’s the last one).

Conclusion. The correct answer is option C.

Source: oracle.com

Monday, August 28, 2023

Going inside Java 21’s rich, upcoming goodness

Final features, continuing previews, and brand-new treats—with video links


Are you ready for all the new technology in Java 21? This article will take you on a tour of many of the changes, small and large, covering final JEPs, a progressing preview, and something entirely new for the platform.

Final features


Virtual threads. Let’s start with the big one: After two rounds of preview with barely any changes, virtual threads are final in Java 21. Now web frameworks are off to the races because they need to let you easily configure using virtual threads instead of platform threads to handle requests.

Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Preparation, Oracle Java Tutorial and Materials, Oracle Java Certification, Oracle Java Learning, Oracle Java Guides

Configuring virtual threads has the potential to let your app handle way more concurrent connections than before. But keep in mind that virtual threads aren’t performance pixie dust, so keep expectations realistic. Then again, if you don’t see the results you’re hoping for, there may be some easy code changes you can do that get you there. Watch episode 23 of the Inside Java Newscast for more on that and some virtual thread guidelines.

Sequenced collections. Many collections in Java have a stable iteration order (all lists and some sets, for example) but don’t necessarily allow indexed access to them (all lists do, but sets usually don’t). Java 21 steps up its collections game and introduces a set of new interfaces that capture this concept and offer related functionality.

At the core of these new interfaces is SequencedCollection, which extends Collection and is ultimately implemented by all lists, some sets, and a few other data structures. It offers the addFirst, addLast, getFirst, getLast, removeFirst, and removeLast methods, which do what you’d expect.

// getting first and last elements from a list
// (sequenced by order of addition)

var letters = List.of("c", "b", "a");
"c".equals(letters.getFirst());
"a".equals(letters.getLast());

// same but from a sorted set
// (sequenced by natural ordering)

var letterSet = new TreeSet<>(letters);
"a".equals(letters.getFirst());
"c".equals(letters.getLast());

There’s also a new method called reversed that returns a SequencedCollection that is a view on the underlying collection but in reverse order, which makes it super easy to iterate or stream over the collection.

var letters = new ArrayList<>(List.of("a", "b", "c"));
var reversedLetters = letters.reversed();

letters.addLast("d");
reversedLetters.forEach(System.out::print);
// ~> dcba

reversedLetters.addFirst("e");
letters.forEach(System.out::print);
// ~> abcde

If you want to learn more about that, the companion interfaces SequencedSet and SequencedMap, and a few odds and ends, check out episode 25 of the Inside Java Newscast.

Generational low-pause garbage collection. Garbage collection is also taking big steps forward. The Z Garbage Collector (ZGC) has a strong focus on ultralow pause times, which can lead to a higher memory footprint or higher CPU usage than other garbage collectors. Starting with Java 21, both of these metrics will be improved on many workloads when ZGC becomes generational, meaning it will maintain separate generations for young objects, which tend to die young, and old objects, which tend to be around for some time.

Preliminary benchmarks show very promising results: In a probably not-representative case, Cassandra 4 showed

◉ Four times the throughput on generational ZGC compared to ZGC with a fixed heap
◉ A quarter of the heap size on generational ZGC compared to ZGC with stable throughput

If you want to give generational ZGC a try on your workload, download a Java 21 early access build and launch it with -XX:+UseZGC -XX:+ZGenerational

Pattern matching. To effectively use pattern matching, you need three things.

◉ A capable switch that allows the application of patterns
◉ The ability to enforce limited inheritance so the switch can check exhaustiveness
◉ An easy way to aggregate and deconstruct data

var shape = loadShape();
var area = switch(shape) {
    case Circle(var r) -> r * r * Math.PI;
    case Square(var l) -> l * l;
    // no default needed
}

sealed interface Shape permits Circle, Square { }
record Circle(double radius) { }
record Square(double length) { }

There are other features that come in really handy (and they are being worked on and one even previews in Java 21—more on that later), but these are the basics, and Java 21 finalizes the last two pieces: pattern matching for switch and record patterns. With these features, you can use this powerful idiom in your projects—be it in a small or large way—if you use a functional or data-oriented approach. To see how these features play together to achieve that, check out episode 29 of the Inside Java Newscast.

Key encapsulation mechanism API. Do you know the Diffie-Hellman key exchange encapsulation (DHKEM) algorithm? If you don’t, you should definitely look into it. On the face of it, the algorithm sounds impossible. It lets two parties compute an encryption key, which is a number, while preventing an observer, who sees every exchanged message, from feasibly redoing the computation, ensuring that the key is a secret that only the two parties know. As you can imagine, that’s very helpful when you need to exchange encrypted information between parties that have no prior knowledge of each other. Hence, the DHKEM algorithm is widely used, for example, to provide forward secrecy in TLS.

Like all key encapsulation mechanisms, DHKEM is a building block of hybrid public key encryption (HPKE) and will be an important tool for defending against quantum attacks. Starting with Java 21, Java has an API to represent key encapsulation mechanisms in a natural way.

Now you’re probably wondering what the API looks like. It’s all described in episode 54 of the Inside Java Newscast with Ana-Maria Mihalceanu.

New view command for JDK Flight Recorder. The JDK Flight Recorder is an amazing piece of tech, and it’s getting better with every JDK release. JDK 21 added the view command, which displays aggregated event data on the terminal. This way, you can view information about an application without the need to dump a recording file or open up JDK Mission Control. Billy Korando explains all in episode 53 of the Inside Java Newscast.

API improvements

Java 21 comes with a number of small additions to existing APIs. Let’s quickly go over them, so you’re aware of where the JDK can do your work for you.

Emoji. The Character class gained a few static checks that let you identify emojis; first and foremost is isEmoji.

var codePoint = Character.codePointAt("😃", 0);
var isEmoji = Character.isEmoji(codePoint);
// prints "😃 is an emoji: true"
System.out.println("😃 is an emoji: " + isEmoji);

Math. The Math class got static clamp methods that take a value, a minimum, and a maximum and return a value that is forced into the [min, max] interval. There are four overloads for the four numerical primitives.

double number = 83.32;
double clamped = Math.clamp(number, 0.0, 42.0);
// prints "42.0"
System.out.println(clamped);

Repeat methods. StringBuilder and StringBuffer gained repeat methods, which allow you to add a character sequence or a code point multiple times to a string that is being built.

var builder = new StringBuilder();
builder.append("Hello");
builder.append(", ");
builder.repeat("World", 3);
builder.append("!");
// prints "Hello, WorldWorldWorld!"
System.out.println(builder);

String. String’s indexOf methods gain overloads that take a maxIndex, as follows:

var hello = "Hello, World";
var earlyCommaIndex = hello.indexOf(",", 0, 3);
// prints "-1"
System.out.println(earlyCommaIndex);

Also, string’s new splitWithDelimiters method behaves like the split method but includes the delimiters in the returned array. The same splitWithDelimiters method was added to Pattern, by the way.

var hello = "Hello; World";
var semiColonSplit = hello.splitWithDelimiters(";", 0);
//prints [Hello, ;,  World]
System.out.println(Arrays.toString(semiColonSplit));

List shuffles. Need to shuffle a List in place with a RandomGenerator? Then that’s your reason to update to Java 21! Once you do, you can pass the list and a RandomGenerator to Collections::shuffle, and it’ll shuffle the list.

var words = new ArrayList<>(List.of("Hello", "new", "Collections", "shuffle", "method"));
var randomizer = RandomGenerator.getDefault();
// using this API makes way more sense when you’re not using the default generator
Collections.shuffle(words, randomizer);
// prints the words above but with a 99.17% chance of a different order
System.out.println(words);

HttpClient. An HttpClient can now be instructed to close, to shut down, or to await termination, but those are best-effort implementations that can have adverse interactions with open request or response body streams.

var httpClient = HttpClient.newHttpClient();
// use the client
httpClient.close();

// or call shutdown and awaitTermination
// yourself for more control:
var httpClient = HttpClient.newHttpClient();
// use the client
httpClient.shutdown();
httpClient.awaitTermination(Duration.ofMinutes(1));

// it also implements AutoCloseable
try (var httpClient = HttpClient.newHttpClient()) {
    // use the client
}

Locales. The Locale.availableLocales() method returns a stream of all available locales.

var locales = Locale
    .availableLocales()
    .map(Locale::toString)
    .filter(locale -> !locale.isBlank())
    .sorted()
    .collect(Collectors.joining(", "));
// prints af, af_NA, af_ZA, af_ZA_#Latn, agq, ...
System.out.println(locales);

Case-folded tags. And because you’ve all asked for case-folded IETF BCP 47 language tags (don’t pretend that you didn’t), Locale gained the caseFoldLanguageTag method.

var lang = Locale.caseFoldLanguageTag("fi-fi");
// prints "fi-FI" (note the RFC5646-correct case)
System.out.println(lang);

Continued evolution

Now it’s time to transition from finalized features to previews, incubators, and experiments. To use a preview feature, you need to add the command-line flag --enable-preview to javac and java, and you also need to specify the Java version for javac, preferably with --release 21.

Structured concurrency. Once you get abundant virtual threads and start creating one virtual thread for every little concurrent task you have, an interesting opportunity arises: You can treat threads that you created for a set of tasks as if they are executing a single unit of work, and you can see them as children of the thread that created them.

An API that capitalizes on that would streamline error handling and cancellation, improve reliability, and enhance observability. And it would make it easy and helpful to start and end that single unit of work in the same scope, defining a unique entry and exit point for handling concurrent code. It would do for concurrency what structured programming did for control flow: add much-needed structure.

Lucky us, because such an API exists! It’s called the Structured Concurrency API.

// create task scope with desired
// error handling strategy
// (custom strategies are possible)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

    // fork subtasks
    Subtask<String> user = scope.fork(() -> findUser());
    Subtask<Integer> order = scope.fork(() -> fetchOrder());

    scope
        // wait for both subtasks
        .join()
        // propagate potential errors
        .throwIfFailed();

    // both subtasks have succeeded
    // ~> compose their results
    // (these calls are nonblocking)
    return new Response(user.get(), order.get());
} // task scope gets shut down

The Structured Concurrency API was incubating in Java 20 and is upgraded to a preview in Java 21. Beyond moving to a proper package, namely java.util.concurrent, the only change has been that StructuredTaskScope’s fork method now returns the new type Subtask. In Java 20, the fork method returned a Future, but that offered degrees of freedom (such as calling the blocking getmethod) that are counterproductive in structured concurrency and was overall too evocative of asynchronous programming, which is exactly what structured concurrency isn’t.

José Paumard has a video tutorial on all this; to see it, check out episode 13 of JEP Café.

Scoped values. The ThreadLocal API is used to store thread-specific information, usually in static final fields, which can then be queried from anywhere those variables are visible. That’s useful, for example, when a container, such as a web server, needs to make information accessible to other parts of its code that it doesn’t call directly. It’s also useful when it doesn’t want to pass that information on explicitly, either for convenience or integrity reasons.

class Server {

    // Principal needs to be visible to other code...
    final static ThreadLocal<Principal> PRINCIPAL = new ThreadLocal<>();

    void serve(Request request, Response response) {
        var level = request.isAuthorized() ? ADMIN : GUEST;
        var principal = new Principal(level);
        PRINCIPAL.set(principal);
        // ... but not the application
        Application.handle(request, response);
    }

}

However, ThreadLocal has a few shortcomings:

◉ Anyone with access to the ThreadLocal field can read its value and also set a new one.

◉ Values stored in ThreadLocal can be inherited from one thread to another. To prevent the other threads from reading an updated value (which the API should explicitly prevent; it’s thread local, after all), the inheriting thread must create copies. These drive up memory use, especially when there are many threads—you know, the whole “millions of virtual threads” thing.

◉ Once set, values must be explicitly removed (using the ThreadLocal::remove method) or they will leak beyond their intended use and continue to occupy memory.

To solve these problems, Java 20 incubated and Java 21 previews the Scoped Values API, which works by binding a value to the ScopedValue instance and passing the code that is allowed to read that value as a lambda—that’s the scope.

class Server {

    final static ScopedValue<Principal> PRINCIPAL = new ScopedValue<>();

    void serve(Request request, Response response) {
        var level = request.isAdmin() ? ADMIN : GUEST;
        var principal = new Principal(level);
        ScopedValue
            // binds principal to PRINCIPAL, but...
            .where(PRINCIPAL, principal)
            // ... only in the scope that is defined by this lambda
            .run(() -> Application.handle(request, response));
    }

}

The Scoped Values API addresses the following ThreadLocal issues:

◉ Within the scope, the bound value is immutable.
◉ Accordingly, no copies need to be created when inheriting, which significantly improves scalability.
◉ As the name implies, a scoped value is visible only within the defined scope; after that, the value is automatically removed, so it cannot accidentally leak.

To see scoped values in practice, watch episode 16 of JEP Café. In it, José Paumard talks about the early version of the API as it was in Java 20 and about what changes in Java 21; besides moving to java.lang, the changes mean the scope (the lambda) can now be a Callable, Runnable, and Supplier.

Vector API. The Vector API is in its sixth incubation and still waiting for Project Valhalla. There’s nothing new to see here, so please move on unless you want to see vectors in action. In that case, check out José Paumard’s episode 18 of JEP Café.

Foreign Function and Memory API. By efficiently invoking code outside the JVM (foreign functions) and by safely accessing memory not managed by the JVM (foreign memory), the Foreign Function and Memory API enables Java programs to call native libraries and process native data without the brittleness and danger of the Java Native Interface (JNI).

One of the main drivers of this API is to provide safe and timely deallocation in a programming language whose main staple is automatic deallocation (thanks, garbage collector).

Finding the right primitive to express this capability in a way that is harmonious with the rest of the Java programming model triggered a round of API changes in Java 20 and again in Java 21, which is why the API will take another round of previewing.

// 1. find foreign function on the C library path
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle radixsort = linker.downcallHandle(stdlib.find("radixsort"), ...);

// 2. allocate on-heap memory to store four strings
String[] words = { "mouse", "cat", "dog", "car" };

// 3. use try-with-resources to manage the lifetime of off-heap memory
try (Arena offHeap = Arena.ofConfined()) {
    // 4. allocate a region of off-heap memory to store four pointers
    MemorySegment pointers = offHeap
        .allocateArray(ValueLayout.ADDRESS, words.length);
    // 5. copy the strings from on-heap to off-heap
    for (int i = 0; i < words.length; i++) {
        MemorySegment cString = offHeap.allocateUtf8String(words[i]);
        pointers.setAtIndex(ValueLayout.ADDRESS, i, cString);
    }

    // 6. sort the off-heap data by calling the foreign function
    radixsort.invoke(pointers, words.length, MemorySegment.NULL, '\0');

    // 7. copy the (reordered) strings from off-heap to on-heap
    for (int i = 0; i < words.length; i++) {
        MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i);
        words[i] = cString.getUtf8String(0);
    }

// 8. all off-heap memory is deallocated at the end of the try-with-resources block
}

On that note, the quality of feedback during the preview phase from projects adopting the API has been excellent and very important for its evolution. If you want to help move Java forward, the easiest way to do that is to experiment with preview features and report back to the respective mailing lists.

Another addition in Java 21 has been the so-called fallback linker, which offers a way for platforms to be compliant with Java SE without too much work by using libffi instead of fully implementing the Linker API.

Goodbye 32-bit Windows port. Microsoft Windows 10 was the last 32-bit version of Windows, and it reaches the end of its lifecycle in October 2025. As no surprise, the Java port for 32-bit Windows isn’t heavily maintained anymore. For example, its implementation of virtual threads isn’t virtual at all—the threads fall back to platform threads. So, I guess it was to be expected that the port got deprecated for removal, which Java 21 does.

Brand-new previews

In Java 21 there are three brand-new preview features that I just can’t skip—and I love how diverse they are! They span from improving a Java workhorse to refining a programming paradigm to changing how beginners learn the language.

Unnamed classes and instance main methods. Java 21 allows for much simpler entry points into a Java program. The main method no longer needs to be public or static, nor does it need the args array. And the whole surrounding class becomes optional, too, making void main the smallest possible Java program.

// content of file Hello.java
void main() {
    System.out.println("Hello, World!");
}

You can watch me demonstrate this in episode 49 of the Inside Java Newscast. Let me briefly clarify two points that I didn’t explain very well in that video.

◉ This is a preview feature, so if you use it in a single source–file program, where it clearly shines, you need to add --enable-preview --source 21 to the java command as follows:

java --enable-preview --source 21 Hello.java

◉ There are plans to shorten System.out.println to just println and to also offer a more succinct way to read from the terminal, but neither of those are part of Java 21.

Unnamed variables and patterns. Unused variables are annoying but bearable. Unused patterns during deconstruction, on the other hand, are really cumbersome and clutter code because they make you want to deconstruct less.

String pageName = switch (page) {
    case ErrorPage(var url, var ex)
        -> "💥 ERROR: " + url.getHost();
    case ExternalPage(var url, var content)
        -> "💤 EXTERNAL: " + url.getHost();
    case GitHubIssuePage(var url, var content, var links, int issueNumber)
        -> "🐈 ISSUE #" + issueNumber;
    case GitHubPrPage(var url, var content, var links, int prNumber)
        -> "🐙 PR #" + prNumber;
};

Therefore, it’s really good that Java 21 turns the underscore into a special variable and pattern, which effectively says, “I won’t be used, and you can have as many of me as you want in the same scope.”

String pageName = switch (page) {
    case ErrorPage(var url, _)
        -> "💥 ERROR: " + url.getHost();
    case ExternalPage(var url, _)
        -> "💤 EXTERNAL: " + url.getHost();
    case GitHubIssuePage(_, _, _, int issueNumber)
        -> "🐈 ISSUE #" + issueNumber;
    case GitHubPrPage(_, _, _, int prNumber)
        -> "🐙 PR #" + prNumber;
};

The change makes code more clearly readable and reduces IDE and compiler warnings. Best of all, though, it makes switching over sealed types more maintainable by allowing you to easily combine default handling of different types into a single branch while avoiding an outright default branch.

String pageEmoji = switch (page) {
    case GitHubIssuePage _ -> "🐈";
    case GitHubPrPage _ -> "🐙";
    // explicitly list remaining types to avoid default
    // branch (only possible with unnamed patterns)
    case ErrorPage _, ExternalPage _ -> "n.a.";
};

If you want to better understand why that’s important and how exactly unnamed variables and patterns work, watch episode 46 of the Inside Java Newscast.

String templates. The practice of embedding variables or simple expressions into strings isn’t popular. One reason is that it’s a bit cumbersome and the code is not perfectly readable. But more importantly, if the embedded content comes from the user, there’s the risk of injection attacks. And generally, unless you’re creating text for humans to read, there’s probably syntax and escaping to consider.

// a cumbersome and dangerous example
String property = "last_name";
String value = "Doe";

String query = "SELECT * FROM Person p WHERE p."
    + property + " = '" + value + "'";

String templates solve those problems. They make it easy to embed expressions in string literals or text blocks by encasing them between an opening backslash followed by an opening curly brace and a closing curly brace. They also enforce processing of such string templates by domain-specific string processors.

Such processors receive the string portions and the variables separately and return instances of any type.

The obvious processor simply concatenates and returns a String, but there are other possibilities out there. A SQL processor could validate and parse a statement’s syntax and return a java.sql.Statement, and a JSON processor could return a JsonNode.

// a safe and readable example
String property = "last_name";
String value = "Doe";

Statement query = SQL."""
    SELECT * FROM Person p
    WHERE p.\{property} = '\{value}'
    """;

If you want to dig deeper, check out episode 47 of the Inside Java Newscast by Ana-Maria Mihalceanu.

Source: oracle.com