Definition
- Strong encapsulation (JEP 396):
sun.*andcom.sun.*internal APIs are blocked by default. Reflection intojava.baserequires--add-opens. The--illegal-accessescape 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:MaxPermSizeinvalid (PermGen gone since Java 8), GC logging changed from-XX:+PrintGCDetailsto-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.*tojakarta.*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.
// FAILS in Java 17: cannot access private field
Field field = String.class.getDeclaredField("value");
field.setAccessible(true); // InaccessibleObjectException!
Fix with --add-opens flag:
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?
# 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.
# 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?
| 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.jarfinds 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
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.
---