首页
文章
归档 时间轴 分类 标签
小工具
工具 待办 图鉴

JDK 版本特性详解(一):JDK 8-11 现代化基础

简介

Java 从 JDK 8 开始进入快速迭代时代,每六个月发布一个新版本。本系列按阶段梳理 JDK 8 到 21 的重要特性,本篇覆盖 JDK 8-11——奠定现代 Java 基础的四个版本,包含两个 LTS(JDK 8 和 JDK 11)。

系列导航:

版本总览

核心特性一览表

本篇覆盖的四个版本:

版本发布时间类型核心特性
JDK 82014-03LTSLambda 表达式
Stream API
Optional 类
新日期时间 API
接口默认方法
JDK 92017-09非LTS模块化系统(Jigsaw)
JShell 交互式工具
集合工厂方法
接口私有方法
Stream API 增强
JDK 102018-03非LTSvar 局部变量类型推断
不可变集合增强
Parallel GC 改进
JDK 112018-09LTSHTTP Client API
字符串新方法
文件读写便捷方法
运行单文件程序

JDK 12 及之后的版本特性见系列后续篇目。

本阶段的两个 LTS

JDK 8 和 JDK 11 都是 LTS(长期支持)版本,是这一阶段生产环境的主要选择:

LTS 版本发布时间支持截止推荐使用场景
JDK 82014-032030-12传统企业系统维护、遗留系统
JDK 112018-092026-09企业级应用、微服务架构
PlantUML Diagram

特性分类

本篇涉及的特性按类别归纳:

特性类别相关版本特性名称主要用途
函数式编程JDK 8Lambda、Stream简化代码、集合操作
空值处理JDK 8Optional安全处理 null,避免 NPE
日期时间JDK 8java.time线程安全的日期时间 API
类型推断JDK 10var减少冗余代码
模块化JDK 9Jigsaw依赖管理、安全性
集合增强JDK 9-10工厂方法/copyOf便捷创建不可变集合
网络JDK 11HTTP ClientHTTP/2、WebSocket
字符串处理JDK 11strip/lines/repeat文本处理便捷方法

一、JDK 8 (2014年3月) - Java 语言的革命性更新

JDK 8 是 Java 历史上最重要的版本之一,引入了函数式编程范式,彻底改变了 Java 的编码风格。

JDK 8 特性概览

特性名称状态功能描述重要程度
Lambda 表达式正式函数式编程,简化匿名内部类
Stream API正式集合的函数式操作,支持链式调用和并行处理
Optional 类正式安全的空值处理,避免 NPE
新日期时间 API正式java.time 包,线程安全、设计合理
接口默认方法正式接口可定义默认实现,支持接口演进

1.1 Lambda 表达式

Lambda 表达式让 Java 支持函数式编程,代码更加简洁优雅。

import java.util.*;

public class LambdaDemo {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
        
        // 传统方式:匿名内部类
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return a.compareTo(b);
            }
        });
        
        // Lambda 表达式方式
        Collections.sort(names, (a, b) -> a.compareTo(b));
        
        // 更简洁的写法
        Collections.sort(names, String::compareTo);
        
        // 遍历集合
        names.forEach(name -> System.out.println(name));
        names.forEach(System.out::println);
    }
}

1.2 Stream API

Stream API 提供了对集合进行函数式操作的能力,支持链式调用和并行处理。

import java.util.*;
import java.util.stream.*;

public class StreamDemo {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // 过滤偶数并求和
        int sum = numbers.stream()
            .filter(n -> n % 2 == 0)   // 过滤偶数
            .mapToInt(n -> n * n)       // 平方
            .sum();                     // 求和
        System.out.println("偶数平方和: " + sum); // 220
        
        // 并行流处理
        long count = numbers.parallelStream()
            .filter(n -> n > 5)
            .count();
        System.out.println("大于5的数量: " + count); // 5
        
        // 收集结果
        List<Integer> filtered = numbers.stream()
            .filter(n -> n > 3)
            .collect(Collectors.toList());
        
        // 分组操作
        Map<Boolean, List<Integer>> partitioned = numbers.stream()
            .collect(Collectors.partitioningBy(n -> n % 2 == 0));
        System.out.println("偶数: " + partitioned.get(true));
        System.out.println("奇数: " + partitioned.get(false));
        
        // 自定义聚合
        String joined = numbers.stream()
            .map(String::valueOf)
            .collect(Collectors.joining(", ", "[", "]"));
        System.out.println(joined); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    }
}

1.3 Optional 类

Optional 解决了空指针异常问题,提供了更安全的空值处理方式。

import java.util.Optional;

public class OptionalDemo {
    public static void main(String[] args) {
        // 创建 Optional
        Optional<String> opt1 = Optional.of("Hello");      // 必须非空
        Optional<String> opt2 = Optional.ofNullable(null); // 可为空
        Optional<String> opt3 = Optional.empty();          // 空值
        
        // 基本操作
        if (opt1.isPresent()) {
            System.out.println(opt1.get());
        }
        
        // 推荐的函数式操作
        opt1.ifPresent(System.out::println);
        
        // 默认值
        String value = opt2.orElse("Default Value");
        String lazyValue = opt2.orElseGet(() -> "Lazy Default");
        
        // 异常处理
        String result = opt2.orElseThrow(() -> 
            new RuntimeException("Value is null"));
        
        // 转换操作
        Optional<Integer> length = opt1.map(String::length);
        Optional<String> filtered = opt1.filter(s -> s.length() > 3);
        
        // 链式调用示例
        User user = getUser();
        String email = Optional.ofNullable(user)
            .map(User::getEmail)
            .orElse("no-email@example.com");
    }
    
    static User getUser() {
        return new User("Alice", "alice@example.com");
    }
}

class User {
    private String name;
    private String email;
    
    User(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
    public String getName() { return name; }
    public String getEmail() { return email; }
}

1.4 新的日期时间 API

JDK 8 引入了全新的日期时间 API,解决了旧 API 的线程安全和设计问题。

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class DateTimeDemo {
    public static void main(String[] args) {
        // LocalDate - 日期
        LocalDate date = LocalDate.now();
        LocalDate specificDate = LocalDate.of(2024, 12, 25);
        
        System.out.println("当前日期: " + date);
        System.out.println("特定日期: " + specificDate);
        
        // 日期运算
        LocalDate nextWeek = date.plusWeeks(1);
        LocalDate lastMonth = date.minusMonths(1);
        LocalDate nextYear = date.plusYears(1);
        
        // LocalTime - 时间
        LocalTime time = LocalTime.now();
        LocalTime specificTime = LocalTime.of(14, 30, 0);
        System.out.println("当前时间: " + time);
        
        // LocalDateTime - 日期时间
        LocalDateTime dateTime = LocalDateTime.now();
        LocalDateTime specificDateTime = LocalDateTime.of(
            2024, 12, 25, 14, 30
        );
        
        // 格式化与解析
        DateTimeFormatter formatter = DateTimeFormatter
            .ofPattern("yyyy-MM-dd HH:mm:ss");
        String formatted = dateTime.format(formatter);
        LocalDateTime parsed = LocalDateTime.parse(
            "2024-12-25 14:30:00", formatter
        );
        
        // Instant - 时间戳
        Instant instant = Instant.now();
        Instant epoch = Instant.EPOCH;
        
        // Duration - 时间差
        Duration duration = Duration.between(time, specificTime);
        long hours = duration.toHours();
        long minutes = duration.toMinutes();
        
        // Period - 日期差
        Period period = Period.between(date, specificDate);
        int days = period.getDays();
        int months = period.getMonths();
        
        // 时区处理
        ZonedDateTime zoned = ZonedDateTime.now();
        ZonedDateTime tokyo = zoned.withZoneSameInstant(
            ZoneId.of("Asia/Tokyo")
        );
        
        // 时钟
        Clock clock = Clock.systemUTC();
        Instant utcInstant = clock.instant();
    }
}

1.5 接口默认方法

接口可以定义默认实现,解决了接口演进的问题。

public interface DefaultMethodDemo {
    // 抽象方法
    void abstractMethod();
    
    // 默认方法
    default void defaultMethod() {
        System.out.println("默认方法实现");
    }
    
    // 静态方法
    static void staticMethod() {
        System.out.println("接口静态方法");
    }
}

class Implementation implements DefaultMethodDemo {
    @Override
    public void abstractMethod() {
        System.out.println("抽象方法实现");
    }
    
    // 可以选择重写默认方法
    @Override
    public void defaultMethod() {
        System.out.println("重写的默认方法");
    }
}

// 多接口默认方法冲突解决
interface InterfaceA {
    default void method() {
        System.out.println("InterfaceA.method");
    }
}

interface InterfaceB {
    default void method() {
        System.out.println("InterfaceB.method");
    }
}

class MultipleInterface implements InterfaceA, InterfaceB {
    @Override
    public void method() {
        // 必须显式选择
        InterfaceA.super.method();
        // 或者 InterfaceB.super.method();
        // 或者自定义实现
    }
}

二、JDK 9 (2017年9月) - 模块化时代

JDK 9 特性概览

特性名称状态功能描述重要程度
模块化系统(Jigsaw)正式解决”JAR地狱”,提升安全性和可维护性
JShell正式交互式编程工具,快速测试代码片段
集合工厂方法正式List.of/Set.of/Map.of 创建不可变集合
接口私有方法正式接口内复用公共逻辑
Stream API 增强正式takeWhile/dropWhile/ofNullable/iterate

2.1 模块化系统 (Project Jigsaw)

JDK 9 最大的变化是引入了模块化系统,解决了”JAR地狱”问题。

// module-info.java
module com.example.myapp {
    requires java.base;              // 默认必需
    requires java.sql;               // 需要 SQL 模块
    requires transitive java.logging; // 传递依赖
    
    exports com.example.api;         // 导出包
    exports com.example.internal to com.example.other; // 限定导出
    
    uses com.example.Service;        // 使用服务
    provides com.example.Service     // 提供服务
        with com.example.impl.ServiceImpl;
}

// 模块中的类
package com.example.api;

public class ApiService {
    public void process() {
        System.out.println("Processing...");
    }
}

2.2 JShell - 交互式编程工具

JShell 提供了交互式的 Java 编程环境。

// 在终端中启动 jshell
// $ jshell

jshell> int x = 10
x ==> 10

jshell> int y = 20
y ==> 20

jshell> int sum = x + y
sum ==> 30

jshell> System.out.println(sum)
30

jshell> List.of(1, 2, 3, 4, 5)
$5 ==> [1, 2, 3, 4, 5]

jshell> $5.stream().filter(n -> n > 2).toList()
$6 ==> [3, 4, 5]

jshell> /save my-session.jsh    // 保存会话
jshell> /open my-session.jsh    // 打开会话
jshell> /exit                   // 退出

2.3 集合工厂方法

更便捷的不可变集合创建方式。

import java.util.*;

public class CollectionFactoryDemo {
    public static void main(String[] args) {
        // 创建不可变 List
        List<String> list = List.of("a", "b", "c");
        // list.add("d"); // UnsupportedOperationException
        
        // 创建不可变 Set
        Set<Integer> set = Set.of(1, 2, 3, 4);
        // Set.of(1, 1, 2); // IllegalArgumentException - 重复元素
        
        // 创建不可变 Map
        Map<String, Integer> map1 = Map.of("a", 1, "b", 2, "c", 3);
        
        // 使用 Map.ofEntries 创建更大的 Map
        Map<String, Integer> map2 = Map.ofEntries(
            Map.entry("a", 1),
            Map.entry("b", 2),
            Map.entry("c", 3),
            Map.entry("d", 4),
            Map.entry("e", 5)
        );
        
        // 注意:这些集合是不可变的
        // 以下操作会抛出 UnsupportedOperationException
        // list.add("d");
        // set.add(5);
        // map1.put("d", 4);
        
        // 如果需要可变集合
        List<String> mutableList = new ArrayList<>(List.of("a", "b", "c"));
        mutableList.add("d"); // 正常工作
    }
}

2.4 接口私有方法

接口可以定义私有方法用于复用代码。

public interface PrivateMethodDemo {
    default void publicMethod1() {
        commonSetup();
        System.out.println("Method 1 execution");
        commonCleanup();
    }
    
    default void publicMethod2() {
        commonSetup();
        System.out.println("Method 2 execution");
        commonCleanup();
    }
    
    // 私有方法 - 公共逻辑复用
    private void commonSetup() {
        System.out.println("Common setup");
    }
    
    private void commonCleanup() {
        System.out.println("Common cleanup");
    }
    
    // 私有静态方法
    private static void staticHelper() {
        System.out.println("Static helper");
    }
}

2.5 Stream API 增强

新增了 takeWhiledropWhileiterate 等方法。

import java.util.stream.*;
import java.util.Optional;

public class StreamEnhancementDemo {
    public static void main(String[] args) {
        // takeWhile - 取到条件不满足为止
        Stream<Integer> takeWhile = Stream.of(1, 2, 3, 4, 5, 6, 3, 2, 1)
            .takeWhile(n -> n < 4);
        System.out.println("takeWhile: " + takeWhile.toList()); // [1, 2, 3]
        
        // dropWhile - 丢弃到条件不满足为止
        Stream<Integer> dropWhile = Stream.of(1, 2, 3, 4, 5, 6, 3, 2, 1)
            .dropWhile(n -> n < 4);
        System.out.println("dropWhile: " + dropWhile.toList()); // [4, 5, 6, 3, 2, 1]
        
        // iterate 的重载版本 - 支持终止条件
        Stream<Integer> iterate = Stream.iterate(1, n -> n <= 10, n -> n + 1);
        System.out.println("iterate: " + iterate.toList()); // [1..10]
        
        // ofNullable - 处理可能为空的元素
        Stream<String> stream1 = Stream.ofNullable(null);
        Stream<String> stream2 = Stream.ofNullable("value");
        
        System.out.println("ofNullable(null): " + stream1.toList()); // []
        System.out.println("ofNullable(value): " + stream2.toList()); // [value]
    }
}

三、JDK 10 (2018年3月) - 局部变量类型推断

JDK 10 特性概览

特性名称状态功能描述重要程度
var 关键字正式局部变量类型推断,减少冗余代码
不可变集合增强正式List.copyOf/Set.copyOf/Map.copyOf
Parallel GC 改进正式G1 GC 性能优化

3.1 var 关键字

引入 var 关键字,让编译器自动推断局部变量类型。

import java.util.*;
import java.util.stream.*;

public class VarDemo {
    public static void main(String[] args) {
        // 基本使用
        var list = new ArrayList<String>();  // 推断为 ArrayList<String>
        var map = new HashMap<String, Integer>(); // 推断为 HashMap<String, Integer>
        var stream = list.stream();          // 推断为 Stream<String>
        
        // 在循环中使用
        for (var element : list) {
            System.out.println(element);
        }
        
        // 在 Stream 中使用
        var result = list.stream()
            .filter(s -> s.length() > 3)
            .collect(Collectors.toList());
        
        // try-with-resources
        try (var input = new Scanner(System.in)) {
            var line = input.nextLine();
        }
        
        // 注意限制
        // ❌ 不能用于字段
        // private var field; // 编译错误
        
        // ❌ 不能用于方法参数
        // public void method(var param) {} // 编译错误
        
        // ❌ 不能用于方法返回类型
        // public var getValue() {} // 编译错误
        
        // ❌ 初始化时不能为 null
        // var x = null; // 编译错误
        
        // ❌ 不能用于 Lambda 表达式
        // var func = () -> {}; // 编译错误
        
        // ✅ Lambda 返回值可以推断
        var runnable = (Runnable) () -> System.out.println("Running");
        
        // ✅ 数组可以推断
        var array = new int[]{1, 2, 3};
    }
}

3.2 不可变集合增强

新增 copyOf 方法,创建不可变集合的副本。

import java.util.*;

public class CollectionCopyDemo {
    public static void main(String[] args) {
        // 创建可变集合
        List<String> mutableList = new ArrayList<>();
        mutableList.add("a");
        mutableList.add("b");
        
        // 创建不可变副本
        List<String> immutableCopy = List.copyOf(mutableList);
        // immutableCopy.add("c"); // UnsupportedOperationException
        
        // 与 List.of 的区别
        // List.of 直接创建新集合
        // List.copyOf 从现有集合创建副本
        
        // Set 和 Map 同样支持
        Set<Integer> setCopy = Set.copyOf(Set.of(1, 2, 3));
        Map<String, Integer> mapCopy = Map.copyOf(Map.of("a", 1));
        
        // 注意:如果原集合已经是不可变的,copyOf 可能直接返回原集合
        List<String> original = List.of("x", "y");
        List<String> copy = List.copyOf(original);
        // 可能 original == copy(同一个对象)
    }
}

3.3 Parallel GC 改进

G1 GC 成为默认垃圾收集器,Parallel GC 性能优化。

// JDK 10 之前
// 默认 GC: Parallel GC (吞吐量优先)

// JDK 10+
// 默认 GC: G1 GC (平衡吞吐量和延迟)

// G1 GC 优势:
// - 更可控的停顿时间
// - 大堆内存支持
// - 并行与并发回收

// 启动参数示例
// java -XX:+UseG1GC MyApp          // 使用 G1 GC
// java -XX:+UseParallelGC MyApp    // 使用 Parallel GC
// java -XX:MaxGCPauseMillis=200    // 设置最大停顿时间

四、JDK 11 (2018年9月) - 长期支持版本

JDK 11 特性概览

特性名称状态功能描述重要程度
HTTP Client API正式支持 HTTP/2 和 WebSocket,替代 HttpURLConnection
字符串新方法正式strip/stripLeading/stripTrailing/isBlank/lines/repeat
文件读写便捷方法正式Files.readString/writeString
运行单文件程序正式java HelloWorld.java 直接运行

4.1 HTTP Client API

全新的 HTTP Client,支持 HTTP/2 和 WebSocket。

import java.net.http.*;
import java.net.URI;
import java.time.Duration;

public class HttpClientDemo {
    public static void main(String[] args) throws Exception {
        // 创建 HTTP Client
        HttpClient client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_2)  // HTTP/2
            .connectTimeout(Duration.ofSeconds(10))
            .followRedirects(HttpClient.Redirect.NORMAL)
            .build();
        
        // GET 请求
        HttpRequest getRequest = HttpRequest.newBuilder()
            .uri(URI.create("https://httpbin.org/get"))
            .header("Accept", "application/json")
            .GET()
            .build();
        
        HttpResponse<String> getResponse = client.send(
            getRequest, HttpResponse.BodyHandlers.ofString()
        );
        System.out.println("GET Status: " + getResponse.statusCode());
        System.out.println("GET Body: " + getResponse.body());
        
        // POST 请求
        HttpRequest postRequest = HttpRequest.newBuilder()
            .uri(URI.create("https://httpbin.org/post"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"test\"}"))
            .build();
        
        HttpResponse<String> postResponse = client.send(
            postRequest, HttpResponse.BodyHandlers.ofString()
        );
        System.out.println("POST Status: " + postResponse.statusCode());
        
        // 异步请求
        client.sendAsync(getRequest, HttpResponse.BodyHandlers.ofString())
            .thenApply(HttpResponse::body)
            .thenAccept(body -> System.out.println("Async: " + body))
            .join();
        
        // 并行请求
        var requests = List.of(
            HttpRequest.newBuilder().uri(URI.create("https://httpbin.org/get")).build(),
            HttpRequest.newBuilder().uri(URI.create("https://httpbin.org/ip")).build()
        );
        
        var responses = requests.stream()
            .map(req -> client.sendAsync(req, HttpResponse.BodyHandlers.ofString()))
            .toList();
        
        responses.forEach(future -> 
            System.out.println(future.join().body())
        );
    }
}

4.2 字符串新方法

新增多个实用的字符串方法。

public class StringMethodsDemo {
    public static void main(String[] args) {
        String text = "  Hello World  ";
        
        // strip - 去除空白(比 trim 更智能,处理 Unicode 空白)
        String stripped = text.strip();
        String strippedLeading = text.stripLeading();
        String strippedTrailing = text.stripTrailing();
        
        System.out.println("strip: '" + stripped + "'");
        System.out.println("stripLeading: '" + strippedLeading + "'");
        System.out.println("stripTrailing: '" + strippedTrailing + "'");
        
        // isBlank - 检查是否为空白字符串
        String blank = "   ";
        System.out.println("isBlank: " + blank.isBlank()); // true
        System.out.println("isEmpty: " + blank.isEmpty()); // false
        
        // lines - 按行分割
        String multiline = "Line 1\nLine 2\nLine 3";
        multiline.lines().forEach(System.out::println);
        
        // repeat - 重复字符串
        String repeated = "abc".repeat(3);
        System.out.println("repeat: " + repeated); // abcabcabc
        
        // strip 与 trim 的区别(Unicode 空白字符)
        String unicodeSpace = "\u2003Hello\u2003"; // EM SPACE
        System.out.println("trim: '" + unicodeSpace.trim() + "'");    // 保留 Unicode 空白
        System.out.println("strip: '" + unicodeSpace.strip() + "'");  // 移除 Unicode 空白
    }
}

4.3 文件读写便捷方法

import java.nio.file.*;

public class FileMethodsDemo {
    public static void main(String[] args) throws Exception {
        Path path = Path.of("test.txt");
        
        // 写文件
        Files.writeString(path, "Hello, JDK 11!\n");
        
        // 读文件
        String content = Files.readString(path);
        System.out.println(content);
        
        // 与旧 API 的对比
        // 旧方式:
        // List<String> lines = Files.readAllLines(path);
        // String content = lines.stream().collect(Collectors.joining("\n"));
        
        // 新方式:一行搞定
        String sameContent = Files.readString(path);
    }
}

4.4 运行 Java 文件

JDK 11 支持直接运行单文件 Java 程序。

# 传统方式
$ javac HelloWorld.java
$ java HelloWorld

# JDK 11 新方式 - 直接运行
$ java HelloWorld.java

# 带参数运行
$ java HelloWorld.java arg1 arg2

# 使用 --source 运行特定版本
$ java --source 11 HelloWorld.java

下一篇

继续阅读 JDK 版本特性详解(二):JDK 12-17 语言增强,了解 Switch 表达式、文本块、Records、Sealed Classes 等特性。

参考资料

100%

Lv.1 亲密度 0