Migrate Java 8 to JDK 17: Must‑Fix Issues and Optimization Tips
This guide outlines the mandatory code changes required when moving from JDK 8 to JDK 17—such as removed modules, internal API restrictions, and compiler flag updates—and highlights optional optimizations like var inference, collection factories, switch expressions, records, text blocks, enhanced Stream and Random APIs, with practical examples and migration steps.
Must‑Fix Changes (Compilation Errors)
When upgrading a Java 8 project to JDK 17, certain APIs and defaults have been removed or altered, which can cause compilation or runtime failures. The following items must be addressed before the code can run on JDK 17.
1. Removed Modules and Classes (Java EE & CORBA APIs)
JDK 11 removed the Java EE and CORBA modules. Commonly used packages include:
javax.xml.bind (JAXB)
javax.activation
javax.annotation
javax.transaction (partial)
javax.jws, javax.xml.ws (JAX‑WS)
Solution: add the required libraries as external dependencies via Maven or Gradle.
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>3.0.1</version>
</dependency>2. Internal API Access Restrictions (Strong Encapsulation)
Direct use of sun.misc.Unsafe or com.sun.* classes may fail under JDK 17 because the runtime now strongly encapsulates internal APIs. To open specific packages you can use the --add-opens JVM option, but the recommended practice is to replace such usage with official public APIs.
java --add-opens java.base/java.lang=ALL-UNNAMED \
--add-opens java.base/java.util=ALL-UNNAMED3. Compilation Target Changes
Since JDK 9 the javac command uses the --release flag instead of separate -source and -target options.
<maven.compiler.release>8</maven.compiler.release>4. Removal of Nashorn JavaScript Engine (JDK 15)
Code that creates a Nashorn engine, e.g. new ScriptEngineManager().getEngineByName("nashorn"), will no longer find the engine. Replace it with GraalVM JavaScript or another external engine such as Rhino.
5. Default Garbage Collector Changes
JDK 8 uses Parallel GC by default, while JDK 9+ switches to G1 GC. Applications sensitive to latency or throughput should revisit GC flags, for example:
# Use Parallel GC
-XX:+UseParallelGC
# Or keep G1 GC
-XX:+UseG1GCOptional Optimizations (Post‑Upgrade Enhancements)
Local variable type inference (var) – JDK 10
var list = new ArrayList<String>(); // inferred as ArrayList<String>Suitable for local temporary variables; avoid overuse in public APIs.
Collection factory methods – JDK 9
List<String> list = List.of("A", "B", "C");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, String> map = Map.of("k1", "v1", "k2", "v2");The created collections are immutable, preventing accidental modification.
Enhanced switch expressions – JDK 14
String result = switch (day) {
case MONDAY, FRIDAY -> "Work";
case SATURDAY, SUNDAY -> "Rest";
default -> throw new IllegalStateException();
};Reduces the risk of missing break statements.
Pattern matching for instanceof – JDK 16
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}No explicit cast is needed.
Record data classes – JDK 16 public record Point(int x, int y) {} Equivalent to a final class with private final fields, accessor methods, and automatically generated equals , hashCode , and toString .
Text blocks – JDK 15
String json = """
{
"name": "ChatGPT",
"type": "AI"
}
""";Multiline strings become more readable.
Stream API enhancements
JDK 8: map, filter, collect JDK 9: takeWhile, dropWhile, overloaded iterate JDK 16: Stream.toList() Example: var list = stream.filter(x -> x > 0).toList(); Stronger Random API – JDK 17
RandomGenerator rng = RandomGeneratorFactory.of("Xoshiro256PlusPlus").create();
int num = rng.nextInt(100);Multiple algorithms are available for better randomness.
Upgrade Recommendation Steps
Compile and run with JDK 11 first (LTS, good compatibility).
Remove all APIs that were removed in later releases.
Adjust internal API accesses (add‑opens or replace with public APIs).
Upgrade to JDK 17 after the codebase builds cleanly on JDK 11.
Enable and refactor to use new language and library features.
Add CI/CD multi‑version testing to ensure builds succeed with both --release 8 and --release 17.
Verify that produced JARs remain backward compatible where required.
Moving from JDK 8 to JDK 17 is only the beginning; the real value comes from applying the new features, testing extensively, and iterating on the code in real‑world scenarios.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ray's Galactic Tech
Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow together!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
