Java 9 新特性(最佳实践)

Java 9 新特性:让代码更简洁、开发更高效

在 Java 的演进历程中,Java 9 是一个承前启后的版本。它不像 Java 8 那样带来革命性的函数式编程支持,也不像 Java 11 那样成为长期支持版本(LTS),但它的改进却极其务实——聚焦于模块化、性能优化和开发体验提升。对于初学者和中级开发者而言,掌握 Java 9 的新特性,不仅能提升编码效率,还能为后续学习 Java 11+ 打下坚实基础。

本文将带你逐个解析 Java 9 中最具价值的几项新特性,用真实代码示例和通俗比喻,帮你快速上手。我们不讲空洞的理论,只讲你写代码时真正能用上的东西。


模块系统:Java 的“包”升级为“系统”

如果你用过 Java 8 或更早版本,一定熟悉 import 语句和 classpath 的概念。但当你项目越来越复杂时,依赖混乱、类冲突、启动慢等问题接踵而至。Java 9 引入的模块系统(Module System)正是为了解决这些“代码臃肿病”。

简单来说,模块系统就像给 Java 应用装上了“防火墙”和“身份证”。每个模块可以明确声明自己依赖哪些其他模块,以及对外暴露哪些类。这就像一个公司,每个部门(模块)只对外公开必要的接口,内部实现细节对外隐藏。

创建模块声明文件 module-info.java

要启用模块化,你需要在项目根目录创建一个 module-info.java 文件。

// module-info.java
module com.example.myapp {
    // 声明该模块依赖 java.base 模块(所有 Java 程序默认依赖)
    requires java.base;

    // 明确声明这个模块对外公开的包
    exports com.example.service;
    exports com.example.util;
}

注释:requires 表示当前模块需要哪些其他模块才能运行。exports 表示哪些包可以被其他模块访问。这是模块系统的核心规则。

实际使用示例

假设你有一个服务类:

// com/example/service/MyService.java
package com.example.service;

public class MyService {
    public void doSomething() {
        System.out.println("服务正在运行...");
    }
}

现在你可以在另一个模块中使用它,前提是那个模块也声明了 requires

// module-info.java(另一个模块)
module com.example.client {
    requires com.example.myapp;  // 显式依赖
    exports com.example.client;
}
// com/example/client/Main.java
package com.example.client;

import com.example.service.MyService;

public class Main {
    public static void main(String[] args) {
        MyService service = new MyService();
        service.doSomething();  // 输出:服务正在运行...
    }
}

注释:如果没有在 module-info.java 中声明 requiresexports,编译器会报错,提示“module not found”或“cannot access”。这正是模块系统防止“误用”的机制。


JShell:交互式 Java 编程环境

你是否曾为测试一段代码而写一个完整的 main 方法?比如测试一个字符串拼接或数学运算?Java 9 推出了 JShell,一个 REPL(Read-Eval-Print Loop)工具,让你像 Python 或 JavaScript 一样,一行一行地执行 Java 代码。

启动 JShell

打开终端,输入:

jshell

你会看到类似这样的提示符:

|  Welcome to JShell -- Type /help for help
|  jshell>

基本用法示例

jshell> int a = 10;
a ==> 10

jshell> int b = 20;
b ==> 20

jshell> int sum = a + b;
sum ==> 30

jshell> System.out.println("和是:" + sum);
和是:30

注释:JShell 会自动保存你定义的变量、方法、类。你可以随时查看它们的值,甚至修改。非常适合快速验证逻辑或学习新语法。

定义方法

jshell> int square(int n) {
   ...>     return n * n;
   ...> }
|  created method square(int)

jshell> square(5)
$2 ==> 25

注释:JShell 支持方法定义、类定义,甚至可以使用 @ 注解。它是学习 Java 9 新语法的绝佳工具。


集合工厂方法:创建不可变集合更简单

在 Java 8 之前,创建不可变集合需要借助 Collections.unmodifiableList() 等包装方法,写起来繁琐且容易出错。Java 9 提供了 List.of()Set.of()Map.of() 等静态工厂方法,让你一行代码搞定不可变集合。

用法示例

// 创建不可变列表
List<String> names = List.of("Alice", "Bob", "Charlie");

// 创建不可变集合
Set<Integer> numbers = Set.of(1, 2, 3, 4, 5);

// 创建不可变映射
Map<String, Integer> scores = Map.of(
    "Alice", 95,
    "Bob", 87,
    "Charlie", 92
);

注释:这些方法返回的集合是不可变的,不能添加、删除或修改元素。如果尝试调用 add()remove() 等方法,会抛出 UnsupportedOperationException。这是防止意外修改数据的好方式。

注意事项

  • List.of()Set.of() 最多支持 10 个元素。
  • 如果需要更多元素,可以使用 List.of() 的变体 List.of(E... elements),支持可变参数。
  • 不能传入 null 值,否则会抛出 NullPointerException

私有接口方法:让接口更“干净”

在 Java 8 之前,接口只能有抽象方法。Java 8 引入了默认方法和静态方法,但不能写私有方法。这导致你如果需要在接口中复用逻辑,只能在实现类中写,或使用内部类。

Java 9 允许在接口中定义私有方法,让接口内部逻辑更清晰。

示例:实现字符串校验逻辑

public interface Validator {
    // 公开的验证方法
    boolean isValid(String input);

    // 私有方法:检查是否为空
    private boolean isEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }

    // 私有方法:检查长度是否在范围内
    private boolean isLengthValid(String str, int min, int max) {
        int len = str.length();
        return len >= min && len <= max;
    }
}

实现类

public class EmailValidator implements Validator {
    @Override
    public boolean isValid(String input) {
        // 使用私有方法组合逻辑
        if (isEmpty(input)) return false;
        if (!isLengthValid(input, 5, 50)) return false;
        return input.contains("@") && input.contains(".");
    }
}

注释:私有方法只能在接口内部被调用,不能被外部实现类直接访问。这避免了代码重复,也防止外部误用。


改进的 Stream API:更高效的流处理

Java 9 在 Stream 接口中新增了几个实用方法,让流处理更灵活。

takeWhile 和 dropWhile

这两个方法用于按条件“截断”流。

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// 取出所有小于 5 的元素
List<Integer> firstFew = numbers.stream()
    .takeWhile(n -> n < 5)
    .toList();

System.out.println(firstFew); // 输出:[1, 2, 3, 4]

// 跳过小于 5 的元素
List<Integer> rest = numbers.stream()
    .dropWhile(n -> n < 5)
    .toList();

System.out.println(rest); // 输出:[5, 6, 7, 8, 9, 10]

注释:takeWhile 会持续取元素,直到遇到第一个不满足条件的元素为止。dropWhile 则跳过满足条件的元素,从第一个不满足的开始保留。

ofNullable

用于处理可能为 null 的值,避免空指针异常。

String value = null;

Stream<String> stream = Stream.ofNullable(value);

// 如果 value 不为 null,会输出 "null";否则不产生任何元素
stream.forEach(System.out::println); // 无输出

注释:ofNullable 是一个安全的流创建方式,特别适合处理外部输入或数据库查询结果。


总结:Java 9 新特性,不只是“新”,更是“好”

Java 9 虽然不是 LTS 版本,但它的新特性真正体现了“为开发者服务”的理念。模块系统让大型项目更可控,JShell 让学习和调试更高效,集合工厂方法简化了常见操作,私有接口方法提升了代码组织能力,Stream API 的增强让数据处理更灵活。

对于初学者来说,从 JShell 入手是个不错的选择,可以快速验证语法;对于中级开发者,掌握模块系统和不可变集合,能显著提升代码质量。

如果你还在使用 Java 8,不妨把 Java 9 当作一次“轻量级升级”——它不会让你重写整个项目,但会让你的代码更清晰、更安全、更现代。

Java 9 新特性,不是堆砌功能,而是让 Java 更“成熟”、更“优雅”。掌握它们,就是为你的开发之路添上一把利器。