Introduction
Java's version history is a fascinating journey through nearly three decades of programming language evolution. From its humble beginnings as a language for interactive television to becoming the backbone of enterprise computing worldwide, each Java release has brought innovations that shaped how we write software today.
Understanding Java's version history isn't just academic curiosity—it's essential practical knowledge for every Java developer. When you encounter legacy codebases, you need to know which features were available in which versions. When planning upgrades, you must understand what breaking changes occurred between releases. When reading job requirements or documentation, version numbers tell you exactly what capabilities to expect.
Java's commitment to backward compatibility is legendary in the software industry. Code written in Java 1.0 in 1996 can still run on Java 21 today—a remarkable achievement that few other languages can claim. This stability, combined with continuous innovation, explains why Java remains one of the world's most popular programming languages after nearly 30 years.
- First Release: Java 1.0 on January 23, 1996
- Current LTS: Java 21 (September 2023)
- Release Cycle: Every 6 months since Java 9 (March and September)
- LTS Cycle: Every 2 years (Java 11, 17, 21...)
- Total Major Versions: 21+ and counting
- Stewarded By: Oracle (since 2010, previously Sun Microsystems)
Understanding Java's Release Model
Before diving into specific versions, it's crucial to understand how Java releases work. This knowledge helps you make informed decisions about which version to use in your projects.
The Two-Track System
/*
* Java Release Timeline Visualization
* =====================================
*
* Feature Releases (every 6 months)
* ─────────────────────────────────────────────────────────────────────▶
* │ │ │ │ │ │ │ │ │ │ │ │
* 9 10 11 12 13 14 15 16 17 18 19 20 21 22
* │ │ │ │
* │ LTS LTS LTS
* │ (3 years) (3 years) (3 years)
* │ │ │ │
* └─────────┴────────────────────────┴───────────────────┴──────────▶
* Long-Term Support Track (updates for years)
*
* Non-LTS versions: Support for 6 months only
* LTS versions: Extended support (typically 8+ years with paid support)
*/
LTS vs Non-LTS: What's the Difference?
| Aspect | LTS Versions | Non-LTS Versions |
|---|---|---|
| Support Duration | Minimum 3 years (often 8+ with Oracle) | 6 months only |
| Security Updates | Years of patches | Until next release |
| Production Use | Recommended | For testing new features |
| Examples | Java 8, 11, 17, 21 | Java 9, 10, 12-16, 18-20 |
For Production: Always use an LTS version (Java 17 or 21 recommended)
For Legacy Systems: Java 8 or 11 if dependencies require it
For Learning/Experimentation: Latest release to explore new features
Never in Production: Non-LTS versions (9, 10, 12-16, 18-20)
The Foundation Era (1996-2004)
These early versions established Java's core identity and fundamental features that remain unchanged today.
Java 1.0 (January 1996) - "Oak"
The first public release, originally developed under the codename "Oak" for interactive television. Java 1.0 introduced the revolutionary concept of "Write Once, Run Anywhere."
- Applets - Run Java in web browsers (revolutionary at the time)
- AWT - Abstract Window Toolkit for GUIs
- Core Classes - java.lang, java.io, java.util basics
- Garbage Collection - Automatic memory management
- Security Model - Sandbox for untrusted code
Java 1.1 (February 1997)
A major improvement that added features still heavily used today:
// Inner classes - new in Java 1.1
public class Outer {
private String message = "Hello";
class Inner {
void printMessage() {
System.out.println(message); // Can access outer class members
}
}
// Anonymous inner classes - event handling pattern
Button button = new Button();
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked!");
}
});
}
// JDBC - Database connectivity
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost/mydb", "user", "pass"
);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
// Reflection - inspect classes at runtime
Class<?> clazz = String.class;
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(m.getName());
}
Java 1.2 (December 1998) - "Java 2"
Such a significant release that Sun rebranded it as "Java 2." This version introduced the Collections Framework, which remains the foundation of Java data structures today.
// The Collections Framework - still used exactly like this today!
List list = new ArrayList(); // Note: no generics yet
Set set = new HashSet();
Map map = new HashMap();
list.add("Hello");
list.add("World");
// Iterator pattern
Iterator it = list.iterator();
while (it.hasNext()) {
String s = (String) it.next(); // Casting required without generics
System.out.println(s);
}
// Swing GUI toolkit - replaced AWT for rich interfaces
JFrame frame = new JFrame("My Application");
JButton button = new JButton("Click Me");
frame.add(button);
frame.setVisible(true);
Java 1.3 (May 2000) - "Kestrel"
- HotSpot JVM became the default - massive performance improvement
- JNDI (Java Naming and Directory Interface) added to core
- Java Sound API for audio processing
- RMI over IIOP for CORBA interoperability
Java 1.4 (February 2002) - "Merlin"
The last version before the major Java 5 overhaul, 1.4 added crucial features:
// assert keyword - Design by Contract
public void setAge(int age) {
assert age >= 0 : "Age cannot be negative";
this.age = age;
}
// Regular expressions - finally!
import java.util.regex.*;
Pattern pattern = Pattern.compile("\\d{3}-\\d{4}");
Matcher matcher = pattern.matcher("Call 555-1234 today");
if (matcher.find()) {
System.out.println("Found: " + matcher.group()); // 555-1234
}
// NIO - Non-blocking I/O
FileChannel channel = FileChannel.open(Paths.get("file.txt"));
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
// Chained exceptions
try {
riskyOperation();
} catch (SQLException e) {
throw new DataAccessException("Failed to access data", e);
}
The Modern Java Era (2004-2014)
Java 5 through 8 transformed Java from a verbose, ceremonial language into a more expressive, modern programming language.
Java 5 (September 2004) - "Tiger" - Revolutionary Release
Java 5 was the most significant update in Java's history up to that point. It introduced features that fundamentally changed how Java code is written.
/*
* GENERICS - Type-safe collections
* Before Java 5, collections stored Objects and required casting
*/
// Before Java 5 (dangerous!)
List oldList = new ArrayList();
oldList.add("Hello");
oldList.add(123); // No compile error - bug waiting to happen!
String s = (String) oldList.get(1); // ClassCastException at runtime!
// Java 5+ (safe!)
List<String> newList = new ArrayList<String>();
newList.add("Hello");
// newList.add(123); // Compile error! Type safety enforced
String str = newList.get(0); // No casting needed
/*
* ENHANCED FOR LOOP - Cleaner iteration
*/
// Before
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
// After
for (String item : array) {
System.out.println(item);
}
/*
* AUTOBOXING - Automatic primitive/wrapper conversion
*/
Integer boxed = 42; // Auto-boxing: int → Integer
int unboxed = boxed; // Auto-unboxing: Integer → int
List<Integer> numbers = new ArrayList<>();
numbers.add(1); // Auto-boxing
int first = numbers.get(0); // Auto-unboxing
/*
* ENUMS - Type-safe constants
*/
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
public boolean isWeekend() {
return this == SATURDAY || this == SUNDAY;
}
}
Day today = Day.MONDAY;
if (!today.isWeekend()) {
System.out.println("Time to work!");
}
/*
* VARARGS - Variable number of arguments
*/
public void printAll(String... messages) {
for (String msg : messages) {
System.out.println(msg);
}
}
printAll("Hello", "World", "!");
/*
* ANNOTATIONS - Metadata for code
*/
@Override
public String toString() {
return "MyClass instance";
}
@Deprecated
public void oldMethod() { }
@SuppressWarnings("unchecked")
public void legacyCode() { }
Java 6 (December 2006) - "Mustang"
Focused on performance and developer productivity rather than language changes:
- Scripting API (JSR 223) - Embed JavaScript, Groovy, etc. in Java
- Compiler API - Compile Java code programmatically
- JDBC 4.0 - Auto-loading of drivers
- Performance - Significant JVM improvements
- Pluggable Annotations - Process annotations at compile time
Java 7 (July 2011) - "Dolphin"
After a long gap, Java 7 brought welcome syntactic improvements:
/*
* DIAMOND OPERATOR - Less verbose generics
*/
// Before Java 7
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
// Java 7+ - type inference
Map<String, List<Integer>> map = new HashMap<>();
/*
* TRY-WITH-RESOURCES - Automatic resource management
*/
// Before Java 7 (verbose and error-prone)
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(path));
return br.readLine();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
// Swallowed exception!
}
}
}
// Java 7+ (clean and safe)
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
} // Automatically closed, even if exception thrown
/*
* MULTI-CATCH - Handle multiple exceptions together
*/
try {
riskyOperation();
} catch (IOException | SQLException | ParseException e) {
logger.error("Operation failed", e);
}
/*
* BINARY LITERALS AND UNDERSCORES
*/
int binary = 0b1010_1010; // Binary literal
long creditCard = 1234_5678_9012_3456L; // Readable numbers
int million = 1_000_000;
/*
* STRINGS IN SWITCH
*/
String day = "Monday";
switch (day) {
case "Monday":
case "Tuesday":
System.out.println("Early week");
break;
case "Friday":
System.out.println("TGIF!");
break;
}
Java 8 (March 2014) - The Functional Revolution
Java 8 is arguably the most important release since Java 5. It brought functional programming to Java and remains the most widely used version in production systems.
/*
* LAMBDA EXPRESSIONS - Functions as first-class citizens
*/
// Before Java 8 - anonymous inner class
Runnable oldWay = new Runnable() {
@Override
public void run() {
System.out.println("Hello!");
}
};
// Java 8 - lambda expression
Runnable newWay = () -> System.out.println("Hello!");
// With parameters
Comparator<String> byLength = (s1, s2) -> s1.length() - s2.length();
/*
* STREAM API - Functional data processing
*/
List<Person> people = Arrays.asList(
new Person("Alice", 30),
new Person("Bob", 25),
new Person("Charlie", 35)
);
// Find names of adults, sorted alphabetically
List<String> adultNames = people.stream()
.filter(p -> p.getAge() >= 18)
.map(Person::getName)
.sorted()
.collect(Collectors.toList());
// Parallel processing - automatic multi-threading
long count = hugeList.parallelStream()
.filter(item -> item.isValid())
.count();
/*
* OPTIONAL - No more NullPointerException
*/
Optional<User> user = userRepository.findById(id);
// Safe access
user.ifPresent(u -> System.out.println(u.getName()));
// Default value
String name = user.map(User::getName).orElse("Unknown");
// Throw if empty
User u = user.orElseThrow(() -> new NotFoundException("User not found"));
/*
* METHOD REFERENCES - Even cleaner lambdas
*/
// Lambda
names.forEach(name -> System.out.println(name));
// Method reference
names.forEach(System.out::println);
// Constructor reference
Supplier<List<String>> listFactory = ArrayList::new;
/*
* DEFAULT METHODS IN INTERFACES
*/
public interface Vehicle {
void start();
// Default implementation - no need to implement in every class
default void honk() {
System.out.println("Beep!");
}
// Static method in interface
static Vehicle create(String type) {
return type.equals("car") ? new Car() : new Bike();
}
}
/*
* NEW DATE/TIME API - Finally, dates done right!
*/
// Immutable and thread-safe
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1990, Month.JANUARY, 15);
LocalDateTime now = LocalDateTime.now();
LocalDateTime meeting = LocalDateTime.of(2026, 2, 15, 10, 30);
// Time zones handled properly
ZonedDateTime zonedNow = ZonedDateTime.now(ZoneId.of("America/New_York"));
// Duration and Period
Duration duration = Duration.between(start, end);
Period age = Period.between(birthday, today);
The Rapid Release Era (2017-Present)
Starting with Java 9, Oracle moved to a six-month release cycle, delivering features faster but requiring more attention to version management.
Java 9 (September 2017) - The Module System
Java 9 introduced the controversial module system (Project Jigsaw), the biggest structural change to Java since its creation.
/*
* MODULE SYSTEM - Modular Java platform
*/
// module-info.java - declares module dependencies
module com.myapp {
requires java.sql; // Needs JDBC
requires java.logging; // Needs logging
requires transitive com.lib; // Passes dependency to users
exports com.myapp.api; // Public API
exports com.myapp.spi to com.plugin; // Restricted export
opens com.myapp.internal to com.framework; // For reflection
}
/*
* COLLECTION FACTORY METHODS
*/
// Before Java 9
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list = Collections.unmodifiableList(list);
// Java 9+ - immutable collections
List<String> list = List.of("a", "b", "c");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("one", 1, "two", 2);
/*
* JSHELL - Interactive Java (REPL)
*/
// $ jshell
// jshell> int x = 5
// x ==> 5
// jshell> x * 10
// $2 ==> 50
/*
* PRIVATE METHODS IN INTERFACES
*/
public interface Logging {
default void logInfo(String msg) {
log("INFO", msg);
}
default void logError(String msg) {
log("ERROR", msg);
}
// Private method for code reuse
private void log(String level, String msg) {
System.out.println("[" + level + "] " + msg);
}
}
/*
* STREAM IMPROVEMENTS
*/
// takeWhile and dropWhile
Stream.of(1, 2, 3, 4, 5)
.takeWhile(n -> n < 4)
.forEach(System.out::println); // 1, 2, 3
// ofNullable
Stream.ofNullable(nullableValue)
.forEach(System.out::println);
Java 10 (March 2018)
/*
* LOCAL VARIABLE TYPE INFERENCE (var)
*/
// Instead of explicit types...
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
// ...use var (type inferred by compiler)
var map = new HashMap<String, List<Integer>>();
var list = List.of("a", "b", "c");
var stream = list.stream();
// Works in loops
for (var item : list) {
System.out.println(item);
}
// NOT allowed: fields, parameters, return types
// private var field; // Error!
Java 11 (September 2018) - LTS
The first LTS release under the new model, Java 11 is widely adopted in production:
/*
* HTTP CLIENT API (Standard)
*/
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.header("Content-Type", "application/json")
.GET()
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString()
);
// Async version
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
/*
* NEW STRING METHODS
*/
" hello ".strip(); // "hello" - better than trim()
" hello ".stripLeading(); // "hello "
" hello ".stripTrailing(); // " hello"
"hello".repeat(3); // "hellohellohello"
"".isBlank(); // true (also " ".isBlank() is true)
"line1\nline2".lines(); // Stream<String>
/*
* VAR IN LAMBDA PARAMETERS
*/
// Allows annotations on lambda parameters
(@NotNull var x, @Nullable var y) -> x + y
/*
* RUN SINGLE-FILE PROGRAMS DIRECTLY
*/
// java HelloWorld.java (no javac needed for single files)
Java 12-16 (2019-2021) - Preview Features
These versions introduced features as previews that became standard in Java 17:
/*
* SWITCH EXPRESSIONS (Java 12-14, standard in 14)
*/
// Before - statement
String result;
switch (day) {
case MONDAY:
case FRIDAY:
result = "Work";
break;
case SATURDAY:
case SUNDAY:
result = "Rest";
break;
default:
result = "Unknown";
}
// After - expression (returns a value)
String result = switch (day) {
case MONDAY, FRIDAY -> "Work";
case SATURDAY, SUNDAY -> "Rest";
default -> "Unknown";
};
// With yield for complex cases
String result = switch (day) {
case MONDAY -> {
log("Starting week");
yield "Work hard";
}
default -> "Regular day";
};
/*
* TEXT BLOCKS (Java 13-15, standard in 15)
*/
// Before - escaped strings
String json = "{\n" +
" \"name\": \"John\",\n" +
" \"age\": 30\n" +
"}";
// After - text blocks
String json = """
{
"name": "John",
"age": 30
}
""";
// HTML, SQL, etc. all become readable
String html = """
<html>
<body>
<h1>Hello, World!</h1>
</body>
</html>
""";
/*
* RECORDS (Java 14-16, standard in 16)
*/
// Before - verbose data class
public class PersonOld {
private final String name;
private final int age;
public PersonOld(String name, int age) {
this.name = name;
this.age = age;
}
public String name() { return name; }
public int age() { return age; }
@Override
public boolean equals(Object o) { /* ... */ }
@Override
public int hashCode() { /* ... */ }
@Override
public String toString() { /* ... */ }
}
// After - one line!
record Person(String name, int age) { }
// Usage
Person p = new Person("Alice", 30);
System.out.println(p.name()); // "Alice"
System.out.println(p); // "Person[name=Alice, age=30]"
/*
* PATTERN MATCHING FOR INSTANCEOF (Java 14-16, standard in 16)
*/
// Before
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// After - binding variable
if (obj instanceof String s) {
System.out.println(s.length()); // s already cast
}
Java 17 (September 2021) - LTS
A major LTS release that standardized many preview features:
/*
* SEALED CLASSES - Restricted inheritance
*/
// Only Circle, Rectangle, and Triangle can extend Shape
sealed interface Shape permits Circle, Rectangle, Triangle { }
final class Circle implements Shape {
private final double radius;
// ...
}
final class Rectangle implements Shape {
private final double width, height;
// ...
}
non-sealed class Triangle implements Shape {
// Can be extended by any class
}
// Compiler knows all possible subtypes - enables exhaustive switch
double area(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> 0.5 * t.base() * t.height();
// No default needed - compiler knows it's exhaustive!
};
}
Java 21 (September 2023) - LTS - The Modern Era
Java 21 brings major features that modernize concurrent programming:
/*
* VIRTUAL THREADS (Project Loom) - Lightweight concurrency
*/
// Before: Platform threads are expensive (1MB stack each)
ExecutorService executor = Executors.newFixedThreadPool(100); // Limited!
// After: Virtual threads are cheap (few KB each)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
// Can handle millions of concurrent tasks!
for (int i = 0; i < 1_000_000; i++) {
executor.submit(() -> {
// Each task gets its own virtual thread
fetchData();
processData();
});
}
}
// Simple virtual thread creation
Thread.startVirtualThread(() -> System.out.println("Hello from virtual thread!"));
// Virtual thread factory
ThreadFactory factory = Thread.ofVirtual().name("worker-", 0).factory();
/*
* RECORD PATTERNS - Destructure records
*/
record Point(int x, int y) { }
record Line(Point start, Point end) { }
void process(Object obj) {
// Nested destructuring!
if (obj instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
System.out.println("Line from (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")");
}
}
// In switch expressions
String describe(Object obj) {
return switch (obj) {
case Point(var x, var y) when x == y -> "Diagonal point";
case Point(var x, var y) -> "Point at " + x + "," + y;
case null -> "null";
default -> "Unknown";
};
}
/*
* SEQUENCED COLLECTIONS - First/last element access
*/
SequencedCollection<String> list = new ArrayList<>();
list.addFirst("first"); // Add at beginning
list.addLast("last"); // Add at end
String first = list.getFirst();
String last = list.getLast();
list.removeFirst();
list.removeLast();
SequencedCollection<String> reversed = list.reversed(); // Reverse view
/*
* STRING TEMPLATES (Preview in 21)
*/
String name = "World";
int x = 10, y = 20;
// String message = STR."Hello, \{name}! Sum is \{x + y}"; // Preview
Complete Version Reference Table
| Version | Release Date | LTS | Key Features |
|---|---|---|---|
| Java 1.0 | Jan 1996 | - | Initial release, Applets, AWT |
| Java 1.1 | Feb 1997 | - | Inner classes, JDBC, Reflection, JavaBeans |
| Java 1.2 | Dec 1998 | - | Collections Framework, Swing, JIT compiler |
| Java 1.3 | May 2000 | - | HotSpot JVM, JNDI, Java Sound |
| Java 1.4 | Feb 2002 | - | assert, regex, NIO, logging, XML |
| Java 5 | Sep 2004 | - | Generics, enums, annotations, varargs, enhanced for |
| Java 6 | Dec 2006 | - | Scripting API, JDBC 4.0, Compiler API |
| Java 7 | Jul 2011 | - | Diamond operator, try-with-resources, multi-catch |
| Java 8 | Mar 2014 | LTS | Lambdas, Streams, Optional, Date/Time API |
| Java 9 | Sep 2017 | - | Modules, JShell, Collection factories |
| Java 10 | Mar 2018 | - | var (local variables) |
| Java 11 | Sep 2018 | LTS | HTTP Client, String methods, single-file execution |
| Java 12-16 | 2019-2021 | - | Switch expressions, text blocks, records (preview) |
| Java 17 | Sep 2021 | LTS | Sealed classes, pattern matching, records (standard) |
| Java 18-20 | 2022-2023 | - | Virtual threads preview, pattern matching enhancements |
| Java 21 | Sep 2023 | LTS | Virtual threads, record patterns, sequenced collections |
Common Pitfalls
Problem: Deploying Java 12, 13, or other non-LTS versions to production.
Why It's Bad: These versions only receive 6 months of updates. After that, no security patches.
Solution: Always use LTS versions (8, 11, 17, 21) in production environments.
Problem: Using deprecated APIs that may be removed in future versions.
// This will be removed!
@Deprecated(since = "9", forRemoval = true)
public void oldMethod() { }
Solution: Compile with -Xlint:deprecation and fix warnings before upgrading.
Problem: Thinking code compiled with Java 17 will run on Java 11.
Why It's Wrong: Java is backward compatible (old runs on new), not forward compatible.
Solution: Always compile for your minimum target version using --release flag.
Interview Questions
Q: What's the difference between Java SE, EE, and ME?
A: SE (Standard Edition) is the core platform for general programming. EE (Enterprise Edition, now Jakarta EE) adds APIs for enterprise features like servlets, JPA, and EJB. ME (Micro Edition) is for embedded devices with limited resources.
Q: What features did Java 8 introduce and why are they significant?
A: Java 8 introduced lambdas, Stream API, Optional, and the new Date/Time API. These brought functional programming to Java, enabling more concise code, better handling of collections, safer null handling, and finally fixing Java's notoriously bad date handling.
Q: What is an LTS version and why does it matter?
A: LTS (Long-Term Support) versions receive security updates and bug fixes for years (typically 8+ years with commercial support). Non-LTS versions only get 6 months of support. Use LTS for production to ensure ongoing security patches.
Q: What problem do Java modules (Java 9+) solve?
A: Modules provide strong encapsulation at the package level, reliable configuration (explicit dependencies), and improved security by hiding internal APIs. They solve "JAR hell" and enable the JDK itself to be modular, allowing smaller runtime images.
Q: What are virtual threads and why are they important?
A: Virtual threads (Java 21) are lightweight threads managed by the JVM rather than the OS. While platform threads are limited (each uses ~1MB of stack), you can create millions of virtual threads. This simplifies concurrent programming by allowing blocking code to scale like async code.
Q: What's the class file version number for Java 17?
A: Java 17 produces class files with major version 61. The JVM uses this to ensure compatibility—older JVMs reject class files with newer version numbers.
Migration Guide
Upgrading from Java 8 to 11
- Modules: Add
--add-modulesor explicit dependencies for Java EE modules (JAXB, JAX-WS) - Reflection: Add
--add-opensfor illegal reflective access - Removed APIs: Replace java.xml.bind with jakarta.xml.bind
- Tools: Update build tools (Maven 3.5+, Gradle 5+)
Upgrading from Java 11 to 17
- Strong Encapsulation: Illegal access denied by default
- New Features: Can start using records, sealed classes, pattern matching
- Security: Stronger defaults, some algorithms removed
Upgrading from Java 17 to 21
- Virtual Threads: Can migrate blocking code to virtual threads
- Pattern Matching: Enhanced patterns in switch
- Sequenced Collections: New collection interfaces
See Also
- What is Java? - Start here if you're new to Java
- JVM Internals - Deep dive into how Java executes
- IDEs - Development environment setup
- Maven - Managing Java versions in builds