Java9功能更新说明

Java9

新增takeWhile()和dropWhile()方法

takeWhile(Predicate)

从流的起始位置开始,逐个提取满足条件的元素,直到遇到第一个不满足条件的元素时停止,得到的是满足条件的列表。

代码示例如下:

java 复制代码
    @Test
    public void takeWhileTest() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 1, 6, 7);
        // 取出所有小于 5 的元素,遇到 5 就停止
        List<Integer> taken = numbers.stream()
                .takeWhile(n -> n < 5)
                .collect(Collectors.toList());
		// 输出结果为:[1, 2, 3, 4]
        System.out.println(taken);
    }
dropWhile(Predicate)

takeWhile 相反,它从流的起始位置开始,逐个丢弃满足条件的元素,直到遇到第一个不满足条件的元素,然后返回包含剩余所有元素的新流。

代码示例如下:

java 复制代码
    @Test
    public void dropWhileTest() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 1, 6, 7);
        // 丢弃所有小于5的元素,从5开始保留
        List<Integer> taken = numbers.stream()
                .dropWhile(n -> n < 5)
                .collect(Collectors.toList());
        // 输出结果为:[5, 1, 6, 7]
        System.out.println(taken);
    }

新增ofNullable()方法

如果传入的元素非 `null`,则返回一个包含该元素的单元素流。 如果传入的元素为 `null`,则返回一个空流。

示例代码如下:

过滤集合中null元素

java 复制代码
    @Test
    public void ofNullableTest() {
        List<String> listWithNulls = Arrays.asList("a", null, "b", null, "c");
        List<String> resultList = listWithNulls.stream()
                .flatMap(Stream::ofNullable)
                .collect(Collectors.toList());
        // 输出结果为:resultList: [a, b, c]
        System.out.printf("resultList: %s\n", resultList);
    }

改进 iterate() 方法

Java 8 中的 Stream.iterate() 方法会生成一个无限流,通常需要配合 limit() 来限制大小。

Java 9 为其增加了一个重载方法,允许直接指定一个终止条件(Predicate),从而更方便地生成有限序列。

  • Java 8 : Stream.iterate(seed, nextFunction)
  • Java 9 : Stream.iterate(seed, hasNextPredicate, nextFunction)

对比示例代码如下:

java 复制代码
    @Test
    public void iterateTest() {
        // Java 8 写法,需要 limit(5) 来限制数量
        Stream.iterate(0, n -> n + 2)
                .limit(5)
                .forEach(System.out::print); // 输出: 0, 2, 4, 6, 8
        System.out.println("\n---------------------------");
        // Java 9 写法,直接在 iterate 中指定终止条件
        Stream.iterate(0, n -> n < 10, n -> n + 2)
                .forEach(System.out::print); // 输出: 0, 2, 4, 6, 8
        System.out.println("\n");
    }

新增Optional.stream()方法

可以将一个 Optional 实例转换为一个 Stream。如果 Optional 有值,则返回包含该值的单元素流;如果为空,则返回空流。

示例代码如下:

可以用来过滤Optional 集合中空元素数据

java 复制代码
    @Test
    public void optionalTest() {
        List<Optional<String>> optionalList = Arrays.asList(
                Optional.of("A"),
                Optional.empty(),
                Optional.of("B")
        );

        // 使用 Java 9 的 Optional.stream() 扁平化并过滤空值
        List<String> result = optionalList.stream()
                .flatMap(Optional::stream)
                .collect(Collectors.toList());
        // 结果: [A, B]
        System.out.println(result);
    }

新增集合工厂方法

集合类型 工厂方法 说明
List List.of(E... elements) 创建不可变列表
Set Set.of(E... elements) 创建不可变集合,元素唯一
Map Map.of(K k1, V v1, ...) 创建不可变映射(最多10个键值对)
Map Map.ofEntries(Map.Entry... entries) 创建任意数量键值对的不可变映射

示例代码:

java 复制代码
    @Test
    public void collectionTest() {
        // 创建不可变 List
        List<String> names = List.of("Alice", "Bob", "Charlie");
        System.out.println(names.stream().collect(Collectors.joining(", ")));
        // 创建不可变 Set
        Set<Integer> numbers = Set.of(1, 2, 3);
        System.out.println(numbers.stream().collect(Collectors.toSet()));
        // 创建不可变 Map (最多10个键值对)
        Map<String, Integer> scores = Map.of("Alice", 95, "Bob", 88);
        System.out.println(scores);
        // 创建超过10个键值对的不可变 Map
        Map<String, Integer> largeMap = Map.ofEntries(
                Map.entry("Java", 1),
                Map.entry("Python", 2),
                Map.entry("Go", 3)
                // ... 可以有更多
        );
        System.out.println(largeMap);
    }

工厂创建的集合特性

  1. 真正不可变 (Immutable)
    集合一旦创建,其内容和结构就不可更改。任何尝试调用 addremoveclear 等修改方法的操作,都会在运行时抛出 UnsupportedOperationException。这与 Java 8 中使用 Collections.unmodifiableList() 等方式创建的"伪不可变"集合不同,后者在编译期无法提供类型安全检查。
  2. 空值安全 (Null-Safe)
    工厂方法在创建集合时会严格检查,不允许任何 null 元素(对于 ListSet)或 null 键/值(对于 Map)。如果传入 null,会立即抛出 NullPointerException,这有助于在开发早期就发现潜在问题。
  3. 元素唯一性 (针对 Set 和 Map)
    对于 Set.of(),如果传入重复元素,会抛出 IllegalArgumentException。同样,对于 Map.of(),如果传入重复的键,也会抛出异常。

多版本JAR文件

Java 9 引入的多版本JAR文件 (Multi-Release JAR Files,简称 MR-JAR)是一项非常重要的特性,旨在解决 Java 平台演进过程中的兼容性痛点

简单来说,它允许你在同一个 JAR 包中打包针对不同 Java 版本编译的类文件。这样,你的库既可以兼容旧版 Java(如 Java 8),又能在运行于新版 Java(如 Java 9、11、17 等)时自动利用新版本的 API 特性。

使用示例

1、添加maven依赖

java 复制代码
    <properties>
        <!-- 定义项目使用的 Maven 和 JDK 版本 -->
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.release>8</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <!-- 1. 配置编译器插件,用于编译不同版本的代码 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <executions>
                    <!-- 执行1: 编译基准版本 (Java 8) -->
                    <execution>
                        <id>default-compile</id>
                        <configuration>
                            <release>8</release>
                        </configuration>
                    </execution>
                    <!-- 执行2: 编译 Java 17+ 版本的代码 -->
                    <execution>
                        <id>java17-compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <release>17</release>
                            <!-- 指定 Java 17 源码的目录 -->
                            <compileSourceRoots>
                                <compileSourceRoot>${project.basedir}/src/main/resources-jdk17</compileSourceRoot>
                            </compileSourceRoots>
                            <!-- 指定 Java 17 编译后的输出目录 -->
                            <outputDirectory>${project.build.outputDirectory}/META-INF/versions/17</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- 2. 配置 JAR 插件,启用多版本打包 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <archive>
                        <!-- 关键配置:在 MANIFEST.MF 中添加 Multi-Release: true -->
                        <manifestEntries>
                            <Multi-Release>true</Multi-Release>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

2、项目结构

IDEA的项目结构

html 复制代码
mrjar
├── src/main/java/com/example/mrjar/Utils.java              <-- 默认版本 (兼容 Java 8)
├── src/main/resources-jdk17/com/example/mrjar/Utils.java   <-- Java 17 专用版本

编译后的项目结构

html 复制代码
mrjar-0.0.1-SNAPSHOT.jar
├── com/example/mrjar/Utils.class          <-- 默认版本 (兼容 Java 8)
├── META-INF/
│   ├── MANIFEST.MF                        <-- 必须包含 "Multi-Release: true"
│   └── versions/
│       ├── 17/
│       │   └── com/example/mrjar/Utils.class  <-- Java 17 专用版本

3、Utils代码示例

Java8

java 复制代码
    public static List<String> getList() {
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");
        return list;
    }

Java17

java 复制代码
    public static List<String> getList() {
        return List.of("6", "7", "8", "9","10");
    }

4、执行

使用如下命令分别在Java8和Java17程序包执行,输出不一样:

bash 复制代码
java -cp mrjar-0.0.1-SNAPSHOT.jar com.example.mrjar.MrJarMain
相关推荐
~|Bernard|1 小时前
三,go语言中channel的底层原理
开发语言·后端·golang
hikktn1 小时前
30万数据导出从2分钟到15秒:一场与内存溢出的生死较量【宗申集团】
java
武帝为此1 小时前
【软件开发日志介绍】
java·前端·数据库
likerhood1 小时前
Java 反射与注解的详细讲解
java·开发语言·数据库
asdfg12589631 小时前
从Java的设计模式看接口和实现---List与ArrayList
java·开发语言·设计模式·面向对象·面向接口
djk88881 小时前
.net swagger api 开启跨域 开启注释
java·前端·.net
云深麋鹿1 小时前
C++ | map&set的使用
开发语言·c++
allnlei1 小时前
gRPC C++ Callback API(Reactor 模式)介绍
开发语言·c++
Eiceblue1 小时前
锁定单元格 :C# 控制 Excel 单元格编辑权限
开发语言·c#·excel