Skip to main content
Innovation|Innovation

Java演进:从Java 8到Java 24有哪些变化及其重要性

从Java 8到24的全面演进指南——涵盖lambda表达式、Stream API、Record类、密封类、虚拟线程、模式匹配以及每个改变我们编写Java方式的重要特性。

2026年4月10日9 min read

从冗长到优雅:Java的十年

Java 8在2014年用lambda和Stream重写了规则。 Since then, every six months a new version ships. Most developers jumped from Java 8 to 11, then 17, now 21. Here is everything that changed — version by version — with the features you will actually use.

Java 8 (2014) — LTS — The Big Bang of Modern Java

Java 8 introduced functional programming to Java. This is the version most developers know deeply.

Lambda表达式和函数式接口

用简洁的语法替代了匿名内部类。 Core interfaces: Predicate<T>, Function<T,R>, Consumer<T>, Supplier<T>.

// Lambda + Predicate composition
Predicate<Product> inStock = p -> p.getQuantity() > 0;
Predicate<Product> isExpensive = p -> p.getPrice() > 100.0;

List<Product> premiumAvailable = products.stream()
    .filter(inStock.and(isExpensive))
    .collect(Collectors.toList());

// All 4 core functional interfaces
Predicate<Employee> isSenior = e -> e.getYears() > 5;         // T -> boolean
Function<Employee, String> fullName = e -> e.getFirstName() + " " + e.getLastName(); // T -> R
Consumer<Employee> sendWelcome = e -> emailService.send(e);   // T -> void
Supplier<Employee> defaultEmp = () -> new Employee("Guest");  // () -> T

Stream API

具有惰性求值的声明式数据处理。 Key operations: filter, map, flatMap, collect, reduce, groupingBy.

// Filter + Map + Collect
List<String> activeNames = employees.stream()
    .filter(e -> e.getStatus() == Status.ACTIVE)
    .map(Employee::getName)
    .sorted()
    .collect(Collectors.toList());

// GroupingBy + downstream counting
Map<Department, Long> headcount = employees.stream()
    .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()));

// FlatMap — flatten nested collections
List<String> allTags = articles.stream()
    .flatMap(a -> a.getTags().stream())
    .distinct()
    .collect(Collectors.toList());

// PartitioningBy — split into true/false groups
Map<Boolean, List<Order>> shipped = orders.stream()
    .collect(Collectors.partitioningBy(Order::isShipped));

Optional

可空值的容器。仅用于返回类型,不要用于字段或参数。

// Safe navigation through nested nullables
String cityName = Optional.ofNullable(customer)
    .map(Customer::getAddress)
    .map(Address::getCity)
    .orElse("UNKNOWN");

// orElseGet is lazy — supplier only runs if empty
Product fallback = products.stream()
    .filter(Product::isAvailable)
    .findFirst()
    .orElseGet(() -> new Product("Default", 0.0));

CompletableFuture

可组合的非阻塞异步操作。

CompletableFuture<Double> priceCheck = CompletableFuture.supplyAsync(() -> fetchPrice(sku));
CompletableFuture<Integer> stockCheck = CompletableFuture.supplyAsync(() -> fetchStock(sku));

// Combine two async results
CompletableFuture<String> summary = priceCheck
    .thenCombine(stockCheck, (price, stock) ->
        "Price: $" + price + ", Stock: " + stock)
    .exceptionally(ex -> "Error: " + ex.getMessage()); // fallback on error

java.time API和默认方法

Immutable, thread-safe date/time classes: LocalDate, LocalDateTime, ZonedDateTime, Instant, Duration. Interfaces gained default methods for backward-compatible evolution.

Java 9 (2017) — Modules & Collection Factories

不可变集合工厂方法

List<String> colors = List.of("red", "green", "blue");       // immutable
Set<Integer> primes = Set.of(2, 3, 5, 7, 11);                // immutable
Map<String, Integer> scores = Map.of("Alice", 95, "Bob", 87); // immutable

Optional和Stream增强

// Optional: ifPresentOrElse, or(), stream()
opt.ifPresentOrElse(v -> process(v), () -> logMissing());
Optional<String> result = opt.or(() -> Optional.of("default"));

// Stream: takeWhile, dropWhile
List<Integer> prefix = Stream.of(1, 2, 3, 10, 4)
    .takeWhile(x -> x < 5)  // [1, 2, 3] — stops at first false
    .toList();

接口私有方法

Helper methods in interfaces for code reuse across default methods.

Java 10 (2018) — var Keyword

// Local variable type inference
var products = new ArrayList<Product>();    // inferred as ArrayList<Product>
var stream = products.stream();              // inferred as Stream<Product>

// Works in for loops
for (var product : products) {
    System.out.println(product.getName());
}

// Unmodifiable copies
var snapshot = List.copyOf(mutableList);  // immutable copy

Constraints: var works only for local variables — not fields, method parameters, or return types. Cannot initialize with null.

Java 11 (2018) — LTS — First LTS After Java 8

// String enhancements
"  hello  ".isBlank();        // false
"  hello  ".strip();          // "hello" (Unicode-aware trim)
"line1\nline2".lines();       // Stream<String>
"ha".repeat(3);               // "hahaha"

// Optional.isEmpty() — opposite of isPresent()
Optional.empty().isEmpty();   // true

// var in lambda parameters (useful for adding annotations)
list.stream()
    .map((@NonNull var s) -> s.toUpperCase())
    .toList();

// Predicate.not() — cleaner negation
list.stream().filter(Predicate.not(String::isBlank)).toList();

Java 12–13 (2019) — Switch Expressions & Text Blocks

// Switch expressions — arrow syntax, no fall-through, yields values
String label = switch (priority) {
    case HIGH, CRITICAL -> "URGENT";
    case MEDIUM         -> "REVIEW";
    case LOW            -> "BACKLOG";
};

// Text blocks — multi-line strings
String query = """
        SELECT id, name, department
        FROM employees
        WHERE status = 'ACTIVE'
        ORDER BY hire_date
        """;

// String.indent() and String.transform()
String result = input.transform(String::strip)
                     .transform(String::toUpperCase);

Java 14 (2020) — Records & Pattern Matching

Record — 不可变数据类

Auto-generates constructor, accessors, equals(), hashCode(), toString().

// One line replaces 50+ lines of boilerplate
public record OrderSummary(String orderId, String customerName,
                           double total, Status status) {}

// Compact constructor for validation
public record Price(double value, String currency) {
    public Price {
        if (value < 0) throw new IllegalArgumentException("Negative price");
        currency = currency.toUpperCase();
    }
}

instanceof的模式匹配

// Before: cast manually
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// After: binding variable in one step
if (obj instanceof String s && s.length() > 5) {
    System.out.println(s.toUpperCase());
}

Java 15 (2020) — Sealed Classes Preview & ZGC Production

Text blocks finalized. ZGC garbage collector moved to production-ready. Sealed classes introduced in preview. Nashorn JavaScript engine removed.

// ZGC — now production-ready, no experimental flag needed
// java -XX:+UseZGC MyApp

// Hidden classes — used internally by frameworks like Spring/Hibernate
// for runtime-generated proxy classes

Java 16 (2021) — Stream.toList() & Records Finalized

// Stream.toList() — cleaner than Collectors.toList()
List<String> names = employees.stream()
    .map(Employee::getName)
    .toList();  // returns unmodifiable list

// mapMulti — alternative to flatMap for conditional one-to-many
Stream.of(1, 2, 3, 4).<String>mapMulti((num, consumer) -> {
    if (num % 2 == 0) {
        consumer.accept(num + " is even");
        consumer.accept(num + " doubled = " + (num * 2));
    }
}).toList();

Java 17 (2021) — LTS — Sealed Classes & Pattern Switch

The current enterprise standard. Sealed classes and pattern matching for switch finalized.

密封类 — 受控继承

public sealed interface PaymentResult
    permits Success, Failure, Pending {
}
public record Success(String txnId, double amount) implements PaymentResult {}
public record Failure(String error, int code) implements PaymentResult {}
public record Pending(String txnId) implements PaymentResult {}

// Exhaustive switch — compiler ensures all cases covered
String describe(PaymentResult result) {
    return switch (result) {
        case Success s  -> "Paid $" + s.amount();
        case Failure f  -> "Failed: " + f.error();
        case Pending p  -> "Pending: " + p.txnId();
    };
}

代数数据类型(ADTs)

Sealed interface + records = sum types from functional programming. The compiler guarantees exhaustiveness — no default case needed.

Java 18 (2022) — UTF-8 Default & Simple Web Server

// UTF-8 is now the default charset on ALL platforms
// No more "works on my machine" charset bugs
System.out.println(Charset.defaultCharset()); // "UTF-8" everywhere

// Built-in simple web server for testing/prototyping
// Command line: jwebserver -p 9000 -d /path/to/files
var server = SimpleFileServer.createFileServer(
    new InetSocketAddress(8080),
    Path.of("/www"),
    OutputLevel.INFO
);
server.start();

// @snippet in Javadoc replaces <pre>{@code ...}</pre>

Java 19 (2022) — Virtual Threads & Structured Concurrency Preview

First preview of Project Loom — the biggest concurrency change since Java 5.

// Virtual threads: lightweight (~KB vs ~MB for platform threads)
Thread.ofVirtual().start(() -> {
    System.out.println("Running in virtual thread");
});

// Handle 100,000 concurrent tasks — impossible with platform threads
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 100_000).forEach(i ->
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        })
    );
}

// Structured concurrency — fan-out pattern
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Subtask<User>    user  = scope.fork(() -> fetchUser());
    Subtask<Account> acct  = scope.fork(() -> fetchAccount());
    scope.join().throwIfFailed();
    return new Profile(user.get(), acct.get());
}

Java 20 (2023) — Refinement Release

All 7 JEPs are previews/incubators. Key addition: Scoped Values — a lightweight, immutable replacement for ThreadLocal designed for virtual threads.

// ScopedValue — replaces ThreadLocal for virtual threads
static final ScopedValue<String> CURRENT_USER = ScopedValue.newInstance();

void handleRequest(String username) {
    ScopedValue.runWhere(CURRENT_USER, username, () -> {
        processRequest(); // all called methods can read CURRENT_USER.get()
    });
}
// Why better? Immutable, auto-inherited by child virtual threads, bounded lifetime

Java 21 (2023) — LTS — Virtual Threads, Record Patterns, Sequenced Collections

The latest LTS. Virtual threads, pattern matching for switch, and record patterns all finalized.

虚拟线程(正式版)

// M:N scheduling — millions of virtual threads on few OS threads
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 1_000_000; i++) {
        executor.submit(() -> {
            // Each task gets its own virtual thread
            return callExternalService();
        });
    }
}

Record模式 — 解构

record Point(int x, int y) {}
record Line(Point start, Point end) {}

// Nested destructuring in switch
String describe(Object obj) {
    return switch (obj) {
        case Line(Point(var x1, var y1), Point(var x2, var y2))
            -> "Line from (%d,%d) to (%d,%d)".formatted(x1, y1, x2, y2);
        case Point(var x, var y) -> "Point at (%d,%d)".formatted(x, y);
        default -> "Unknown";
    };
}

有序集合

// New methods on ordered collections
List<String> list = List.of("a", "b", "c");
list.getFirst();   // "a"
list.getLast();     // "c"
list.reversed();   // ["c", "b", "a"]

// Also works on LinkedHashSet, LinkedHashMap, Deque

Java 22 (2024) — Unnamed Variables & FFM API

未命名变量(_

// Intentionally unused variable — no more "unused variable" warnings
try (var _ = ScopedContext.acquire()) { /* don't need the ref */ }
for (var _ : collection) { count++; }
map.forEach((_, value) -> process(value));
try { riskyOp(); } catch (Exception _) { log("failed"); }

// In pattern matching
if (obj instanceof Point(var x, _)) { /* only care about x */ }

Stream Gatherers(预览版)

// Sliding window
List<List<Integer>> windows = Stream.of(1, 2, 3, 4, 5)
    .gather(Gatherers.windowSliding(3)).toList();
// [[1,2,3], [2,3,4], [3,4,5]]

// Fixed-size batches
List<List<Integer>> batches = Stream.of(1,2,3,4,5,6,7)
    .gather(Gatherers.windowFixed(3)).toList();
// [[1,2,3], [4,5,6], [7]]

外部函数和内存API(正式版)

Safe replacement for JNI. Call native C functions directly from Java with memory safety guarantees.

Java 23 (2024) — Markdown Javadoc & ZGC Default

模式中的原始类型(预览版)

String describe(int statusCode) {
    return switch (statusCode) {
        case 200 -> "OK";
        case 404 -> "Not Found";
        case int i when i >= 500 -> "Server Error: " + i;
        case int i -> "Other: " + i;
    };
}

Markdown文档注释

/// Returns the **full name** of the user.
///
/// This method combines:
/// - First name
/// - Last name
///
/// @param user the user object
/// @return the concatenated full name
public String getFullName(User user) { ... }

模块导入声明(预览版)

// One import for an entire module
import module java.base;  // imports java.util, java.io, java.nio, etc.

Note: String Templates (previewed in 21–22) were withdrawn in Java 23 due to design issues.

Java 24 (2025) — Stream Gatherers Finalized, AOT, Virtual Thread Unpinning

The largest release ever with 24 JEPs.

Stream Gatherers(正式版)

// Built-in: windowFixed, windowSliding, fold, scan, mapConcurrent
List<Response> responses = urls.stream()
    .gather(Gatherers.mapConcurrent(5, this::httpGet))
    .toList();

// Custom gatherer: distinct by key
static <T, K> Gatherer<T, ?, T> distinctBy(Function<T, K> keyExtractor) {
    return Gatherer.ofSequential(HashSet::new,
        (seen, element, downstream) -> {
            if (seen.add(keyExtractor.apply(element)))
                return downstream.push(element);
            return true;
        });
}
// Usage: people.stream().gather(distinctBy(Person::lastName)).toList();

预编译类加载(Project Leyden)

# Record → Build cache → Launch with up to 42% faster startup
java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf -cp app.jar Main
java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf -XX:AOTCache=app.aot -cp app.jar
java -XX:AOTCache=app.aot -cp app.jar Main

虚拟线程解钉

Virtual threads no longer get pinned to carrier threads in synchronized blocks. This was the biggest limitation of virtual threads in Java 21–23.

// Java 21–23: synchronized + blocking I/O PINNED the carrier thread
// Java 24: virtual thread unmounts — carrier is free for other work
synchronized (sharedResource) {
    return fetchFromDatabase(); // no longer pins! Major perf improvement
}

Class-File API(正式版)

Standard API for parsing and generating .class files. Replaces third-party libraries like ASM.

其他值得注意的变化

Security Manager permanently disabled. Compact object headers (experimental, 10–20% heap reduction). Generational Shenandoah GC (experimental).

LTS路线图与版本选择

VersionYearTypeHeadline Feature
Java 82014LTSLambdas, Streams, Optional
Java 112018LTSString enhancements, var in lambda, HttpClient
Java 172021LTSSealed classes, pattern matching, records finalized
Java 212023LTSVirtual threads, record patterns, sequenced collections
Java 25Sep 2025LTSNext LTS — expected to finalize structured concurrency, scoped values

Recommendation: For new projects, target Java 21 (current LTS). For enterprises still on Java 8 or 11, plan migration to 21. Wait for Java 25 (September 2025) if you need the next LTS.

核心要点

Java 8→11: Functional foundations — lambdas, streams, var, string helpers.

Java 12→17: Boilerplate elimination — records, sealed classes, switch expressions, text blocks, pattern matching.

Java 18→24: Concurrency revolution — virtual threads, structured concurrency, scoped values, stream gatherers, AOT compilation.

Java is no longer the verbose language it was. Modern Java (17+) is concise, expressive, and performs better than ever.

More from Innovation