Back to Java 17 Features
Q2Question 2 of 3

What are Java 8 to 17 breaking changes?

Key Concepts
  • Strong encapsulation (JEP 396): reflective access to JDK internals is denied by default in Java 17
  • Removed modules: JAXB, JAX-WS, CORBA, Nashorn, Pack200 are gone
  • Removed JVM flags: CMS GC removed entirely, -XX:MaxPermSize no longer valid (PermGen gone since Java 8)
  • Default GC changed from Parallel to G1 since Java 9; tuning parameters may differ
  • Framework bumps: Spring 5 to 6 (javax to jakarta namespace), Hibernate 5 to 6
  • module-info.java is NOT required: classpath applications run on Java 17 as unnamed modules

Definition

Five breaking changes from Java 8 to 17: strong encapsulation blocks reflection into JDK internals, removed modules need Jakarta dependencies, CMS GC and other JVM flags are gone, G1 is the default GC, and frameworks require version bumps. module-info.java is not required.
  • Strong encapsulation (JEP 396): sun.* and com.sun.* internal APIs are blocked by default. Reflection into java.base requires --add-opens. The --illegal-access escape hatch was removed in Java 17.
  • Removed modules: JAXB, JAX-WS, CORBA, Nashorn (JavaScript engine), Pack200 are deleted. Add Jakarta dependencies.
  • Removed JVM flags: CMS GC removed (-XX:+UseConcMarkSweepGC), -XX:MaxPermSize invalid (PermGen gone since Java 8), GC logging changed from -XX:+PrintGCDetails to -Xlog:gc.
  • Default GC: changed from Parallel to G1 (Java 9+). New collectors ZGC and Shenandoah are available.
  • Framework bumps: Spring 5 to 6 requires javax.* to jakarta.* migration. Hibernate 5 to 6 for Java 17 support.
  • module-info.java: NOT required. Classpath applications run as unnamed modules without modularization.

Moving to a new bank building: some internal doors are now locked (strong encapsulation) and need a special key (--add-opens). The cafeteria moved to a separate building (removed modules need external dependencies). Some old equipment does not fit new power outlets (removed JVM flags). The default air conditioning changed (G1 instead of Parallel GC). But you do NOT need to redesign your office layout (no module-info.java required).

Understanding the Concepts

What is strong encapsulation?

Java 17 enforces strong encapsulation of JDK internal APIs. Code that uses sun.*, com.sun.*, or reflects into private fields of JDK classes fails with InaccessibleObjectException.

JAVA
// FAILS in Java 17: cannot access private field
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);  // InaccessibleObjectException!

Fix with --add-opens flag:

BASH
java --add-opens java.base/java.lang=ALL-UNNAMED -jar app.jar

Or better: upgrade the library that uses internal APIs.

What modules were removed?

| Module | Removed In | Replacement |
|--------|-----------|-------------|
| java.xml.bind (JAXB) | Java 11 | jakarta.xml.bind:jakarta.xml.bind-api |
| java.xml.ws (JAX-WS) | Java 11 | jakarta.xml.ws:jakarta.xml.ws-api |
| java.corba | Java 11 | N/A (deprecated technology) |
| Nashorn JS engine | Java 15 | GraalJS or Rhino |
| Pack200 | Java 14 | N/A |
| Security Manager | Deprecated Java 17 | N/A (being phased out) |

What JVM flags changed?

BASH
# REMOVED: CMS garbage collector
-XX:+UseConcMarkSweepGC        # error in Java 17
-XX:+CMSIncrementalMode          # error

# REMOVED: PermGen (gone since Java 8)
-XX:MaxPermSize=256m             # ignored/warning

# CHANGED: GC logging
-XX:+PrintGCDetails              # old, removed
-Xlog:gc*                        # new unified logging

Do you need module-info.java?

No. Classpath applications run on Java 17 as "unnamed modules" without any modularization. You only need module-info.java if you want to create named modules for strong encapsulation of your own code.

BASH
# This works fine on Java 17 without module-info.java
java -cp banking-service.jar com.bank.Main
java -jar banking-service.jar

What framework versions are needed?

| Framework | Java 8 Version | Java 17 Version | Key Change |
|-----------|---------------|-----------------|------------|
| Spring Boot | 2.x | 3.x | javax to jakarta |
| Hibernate | 5.x | 6.x | javax to jakarta |
| JUnit | 4.x | 5.x | New API |
| Mockito | 3.x | 5.x | Java 17 support |
| Lombok | 1.18.x | 1.18.30+ | Java 17 compat |

What are common mistakes?

WRONG: Adding --add-opens for everything instead of upgrading libraries.

WRONG: Assuming module-info.java is required (it is not).

WRONG: Not updating the Docker base image from Java 8 to Java 17.

What is the migration checklist?

DIAGRAM
| Step | Action | Tool |
|------|--------|------|
| 1 | Audit JDK internal API usage | `jdeps --jdk-internals` |
| 2 | Add removed module dependencies | Maven/Gradle |
| 3 | Update JVM flags | Remove CMS, update GC logging |
| 4 | Upgrade frameworks | Spring 5->6, Hibernate 5->6 |
| 5 | Fix javax to jakarta imports | IDE refactor |
| 6 | Run tests | Full regression suite |

What should a senior engineer know?

  • The riskiest breakages are transitive libraries doing deep reflection, not your own code.
  • jdeps --jdk-internals your.jar finds internal API usage proactively.
  • ZGC (low-latency, <1ms pauses) and Shenandoah (similar) are available in Java 17 for latency-sensitive banking applications.
  • The javax to jakarta namespace change is a find-and-replace at the import level, but Spring Boot 3.x handles it for most Spring-managed classes.

Visual Overview

DIAGRAM
  Breaking Changes: Java 8 → 17

  ┌────────────────────────────────────────────────┐
  │ 1. Strong Encapsulation (JEP 396)              │
  │    sun.* / com.sun.* blocked                   │
  │    Fix: --add-opens or upgrade library          │
  │                                                │
  │ 2. Removed Modules                             │
  │    JAXB, JAX-WS, CORBA, Nashorn                │
  │    Fix: add Jakarta dependencies               │
  │                                                │
  │ 3. Removed JVM Flags                           │
  │    CMS GC, PermGen flags, old GC logging       │
  │    Fix: use G1/ZGC, -Xlog:gc                   │
  │                                                │
  │ 4. Default GC: Parallel → G1                   │
  │    May need tuning for throughput workloads     │
  │                                                │
  │ 5. Framework Bumps                             │
  │    javax.* → jakarta.*                          │
  │    Spring 5→6, Hibernate 5→6                   │
  │                                                │
  │ ✓ module-info.java NOT required                │
  └────────────────────────────────────────────────┘

Key Takeaway

The five Java 8 to 17 breaking changes are: strong encapsulation blocks reflection into JDK internals (fix with --add-opens or library upgrades), removed modules need explicit Jakarta dependencies, several JVM flags like CMS GC are removed, the default GC switched from Parallel to G1, and major frameworks require version bumps with the javax to jakarta namespace change.

You do NOT need module-info.java. Classpath applications run on Java 17 as unnamed modules without any modularization. The most impactful change in practice is the javax to jakarta namespace migration when upgrading to Spring Boot 3.x.

At National Bank of India, the migration team uses jdeps --jdk-internals to audit the codebase, fixes removed module dependencies, and upgrades Spring Boot from 2.x to 3.x with the namespace change.

Remember one sentence: strong encapsulation, removed modules, and javax-to-jakarta are the three big breaking changes, and module-info.java is not required.

---

Share
Use ← → arrow keys to navigate