-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj
0DAQcDQgAEi/kRGOL7wCPTN4KJ
2ppeSt5UYB6ucPjjuKDtFTXbgu
OIFDdZ65O/8HTUqS/sVzRF+dg7
H3/tkQ/36KdtuADbwQ==
-----END PUBLIC KEY-----JDK 28 will be released in March 2027
(early-access builds at jdk.java.net/28).
Feature freeze in December 2026.
Integrated changes (as of today):
value objects (preview)
default generational Shenandoah
strict field initialization (preview)
simple JSON API (incubator)
macOS/x64 port deprecation
PEM encodings
Ongoing previews that will continue or finalize:
lazy constants
primitive types in patterns
structured concurrency
vector API
strict field initialization (preview)
default generational Shenandoah
macOS/x64 port deprecation
A JVM preview feature for safer interaction with fields:
compiler can mark fields as strict
runtime ensures no reads before first write
runtime ensures single write for strict final fields
(i.e. reflection can’t mutate them)
Marking fields as strict:
is generally incompatible for regular classes
is done for value and record instance fields
Shenandoah is:
a low-pause-time GC
Red Hat’s alternative to Oracle’s ZGC
not available in Oracle JDK
Generational mode:
improves most GC metrics
introduction in JDK 24/25 (ZGC: 21)
default in JDK 28 (ZGC: 23)
will be the only mode in the future (ZGC: 24)
Support for x64:
macOS 27 won’t be available for x64
macOS 26 support will end in 2028
Implications for Java:
no JDK 27+ port for macOS/x64
JDK 28 deactivates macOS/x64 buids
older Oracle JDK LTS versions
keep supporting macOS/x64
Representations of cryptographic objects
(keys, certificates, certificate revocation lists):
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj
0DAQcDQgAEi/kRGOL7wCPTN4KJ
2ppeSt5UYB6ucPjjuKDtFTXbgu
OIFDdZ65O/8HTUqS/sVzRF+dg7
H3/tkQ/36KdtuADbwQ==
-----END PUBLIC KEY-----PEM API encodes and decodes cryptographic objects:
X509Certificate cert = // ...
// encode
PEMEncoder encoder = PEMEncoder.of();
String pem = encoder.encodeToString(cert);
// decode
PEMDecoder decoder = PEMDecoder.of();
BinaryEncodable cert2 = decoder.decode(pem);
assert cert.equals(cert2);All implementations of BinaryEncodable can be encoded:
AsymmetricKey
(DH, DSA, EC, RSA, etc.)
KeyPair
PKCS8EncodedKeySpec
X509EncodedKeySpec
X509Certificate
X509CRL
EncryptedPrivateKeyInfo
PEM
For decoding:
if the object type is known, call
decode(String, Class<T extends BinaryEncodable>):
KeyPair kp = decoder.decode(pem, KeyPair.class);otherwise, switch over return value of decode(String)
PEMEncoder/PEMDecoder instances are
immutable, thread-safe, and reusable
overloads for decoding from InputStream exist
unknown cryptographic objects are decoded to PEM
PrivateKey, KeyPair, PKCS8EncodedKeySpec
instances can be encrypted/decrypted
The PEM API is final in JDK 28.
📝 JEP 542: PEM Encodings of Cryptographic Objects
You’ve probably heard a lot about Project Valhalla:
indirection and flattening
object headers and density
identity and custom primitives
universal and specialized generics
performance benefits
Forget all that for now!
Current state:
all reference types have identity
identity distinguishes objects
many equal immutable objects are interchangeable
(e.g. two LocalDate.of(2026, 9, 9))
For many immutable classes:
identity is meaningless
identity can cause confusion
identity comes with a run-time cost
Wouldn’t it be nice to be able to opt out of identity?
Enter JEP 401 in JDK 28 with --enable-preview:
We introduce value objects to model simple immutable data. A value object is an instance of a value class, declared with the
valuemodifier. Classes without the value modifier are identity classes, and their instances are identity objects.
With previews enabled, 30 JDK classes are value classes:
primitive wrappers
Optional types
many date/time API types
You can create your own by adding value:
value class ComplexNumber {
// ...
}
value record Point(int x, int y) {
// ...
}(Fun fact: The only new syntax is value.)
In most respects, value objects work the way that objects have always worked in the language: They have fields and methods, they are handled by reference, and their references can be
null.
Declaration site restrictions:
fields and class are final
can’t extends identity classes
(except java.lang.Object)
by default, construction code runs
during early construction
Behavioral differences vs identity classes:
new may not allocate a fresh object
== does a field-wise comparison
Behavioral restrictions:
no deep reflection
no object monitor interaction
no finalization
limited serialization (records or proxies)
no GC interaction
What other benefits can we get?
Optimization of value objects:
JVM can flatten and scalarize some references
flattening is limited to small values
scalarization is more universally applicable
But, as (almost) always in Java:
write maintainable code
use constructs semantically
rely on JVM for optimization
Value classes have their first preview in JDK 28.
📝 JEP 401: Value Objects (Preview)
📝 State of Valhalla
🎥 Valhalla - Java’s Epic Refactor (Dec 2024)
🎥 Growing the Java Language (Aug 2025)
A simple API to parse, traverse, and format JSON:
var jsonDoc = """
{
"users": [
{ "name": "John Doe" },
{ "name": "Jane Doe" }
]
}
""";
JsonValue json = Json.parse(jsonDoc);public sealed interface JsonValue permits
JsonObject, JsonArray, JsonNull
JsonString, JsonNumber, JsonBoolean {
// ...
}String asString();
double asDouble();
long asLong();
int asInt();
boolean asBoolean();
List<JsonValue> asList();
Map<String, JsonValue> asMap();
JsonValue get(String name);
JsonValue get(int index);
Optional<JsonValue> tryGet(String name);
Optional<JsonValue> tryValue();JsonValue json = Json.parse(jsonDoc);
// if structure is known
var name = json
.get("users")
.get(0)
.get("name");
var names = json
.get("users")
.asList().stream()
.map((JsonValue user) -> user.get("name"))
.toList();JsonValue json = Json.parse(jsonDoc);
// if structure is unknown
switch (json) {
case JsonString string -> // ...
case JsonNumber number -> // ...
// ...
}This API is a "JSON starter set":
no parsing configuration
no streaming
no data binding
Advanced features are left to ecosystem projects.
The JSON API is incubating for the first time in JDK 28.
Get JDK 28 EA builds at jdk.java.net/28
(unless you’re on an x64 Mac 😉):
use the PEM text API
try out value objects and the JSON API
(don’t write more code than you can rewrite/delete)
benefit from hundreds of small improvements
(API refinements, bug fixes, optimizations, etc.)