大家好!今天我要和大家分享 Java 9 中那些真正改变我们编码方式的新特性。作为 Java 开发者,了解这些新功能不仅能让你的代码更简洁、更高效,还能帮助你在团队中脱颖而出。
Java 9 于 2017 年 9 月发布,它带来了自 Java 8 以来最重大的架构变革。与 Java 8 注重语法层面的革新(如 Lambda 表达式)不同,Java 9 更关注基础设施和平台级别的改进,为大型应用开发、微服务架构和模块化编程提供了强大支持。
废话不多说,让我们直接进入正题!
1. 模块系统(Project Jigsaw)
Java 9 最重要的变革无疑是引入了模块系统,这是 Java 平台级别的重大架构改变。
什么是模块系统?
模块系统允许我们将代码组织成更高级别的组件(称为模块),每个模块都明确声明自己的依赖关系和对外公开的 API。
使用案例
让我们创建一个简单的模块化应用程序:
java
// 在module-info.java文件中定义模块
module com.example.myapp {
requires java.base; // 隐式依赖,可省略
requires java.logging;
requires transitive java.sql; // 传递依赖,使用此模块的模块也能访问java.sql
exports com.example.myapp.api; // 只导出API包
opens com.example.myapp.model to java.persistence; // 仅向特定模块开放反射访问
}
java
// com.example.myapp.api包中的公共API
package com.example.myapp.api;
public class ServiceAPI {
public String getServiceInfo() {
return "Service Information";
}
}
java
// com.example.myapp.internal包中的内部实现
package com.example.myapp.internal;
import java.util.logging.Logger;
class ServiceImplementation {
private static final Logger LOGGER = Logger.getLogger(ServiceImplementation.class.getName());
void processData() {
LOGGER.info("Processing data...");
// 实现细节
}
}
使用 Maven 构建模块化项目
xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>myapp</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>9</maven.compiler.source>
<maven.compiler.target>9</maven.compiler.target>
<maven.compiler.release>9</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>9</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
使用 jlink 创建自定义运行时
css
jlink --module-path $JAVA_HOME/jmods:target/modules --add-modules com.example.myapp --launcher myapp=com.example.myapp/com.example.myapp.Main --output myapp-image
上述命令创建一个只包含应用程序所需模块的自定义 JRE,大小可能只有标准 JRE 的一小部分。
模块系统优势分析
- 强封装性:外部代码无法访问未明确导出的包,提高代码安全性
- 明确依赖:模块必须声明其依赖关系,消除"classpath 地狱"
- 更小的运行时映像 :使用
jlink
可以创建仅包含所需模块的自定义运行时 - 提高平台完整性:JDK 本身被模块化,增强了安全性
- 更好的性能:启动时间更短,内存占用更少
注意事项与局限性
- 遗留代码兼容性:旧代码需作为未命名模块放在类路径上,不能充分利用模块系统优势
- 迁移复杂性:大型项目迁移到模块系统可能需要大量重构
- 依赖爆炸:如果不谨慎管理传递依赖,可能导致模块图变得复杂
- 反射限制 :默认情况下,非导出包不允许反射访问,需使用
opens
显式开放
迁移策略
对于现有项目,推荐采用以下迁移路径:
- 作为自动模块:先将 jar 放在模块路径而非类路径,成为自动模块
- 添加 module-info.java:逐步为每个组件添加模块描述
- 拆分模块:根据内聚性原则重构为多个模块
- 管理依赖:重新考虑 exports 和 requires 语句,确保最小化暴露
2. JShell - Java 的交互式 REPL 环境
Java 9 引入了令人期待已久的 REPL(Read-Eval-Print Loop)工具,让我们能快速测试 Java 代码片段。
使用案例
启动 JShell 很简单:
bash
$ jshell
| Welcome to JShell -- Version 9
| For an introduction type: /help intro
基本代码测试:
java
jshell> int x = 10
x ==> 10
jshell> int y = 20
y ==> 20
jshell> x + y
$3 ==> 30
jshell> String greeting = "Hello, Java 9!"
greeting ==> "Hello, Java 9!"
jshell> greeting.toUpperCase()
$5 ==> "HELLO, JAVA 9!"
高级用法 - 导入外部类和定义类:
java
// 导入外部包
jshell> import java.util.concurrent.CompletableFuture
// 定义类
jshell> class Person {
...> private String name;
...> private int age;
...>
...> public Person(String name, int age) {
...> this.name = name;
...> this.age = age;
...> }
...>
...> public String toString() {
...> return name + ", " + age;
...> }
...> }
| 已创建 类 Person
// 使用刚定义的类
jshell> Person p = new Person("张三", 30)
p ==> 张三, 30
// 加载外部脚本
jshell> /open MyUtils.java
JShell 关键优势
- 快速测试和学习:无需编写完整类就能执行代码
- 即时反馈:立即看到执行结果
- API 探索:快速尝试和理解 API 功能
- 教学工具:特别适合学习和教学环境
- 原型设计:快速验证算法或设计想法
局限性
- 性能:不适合性能测试,因为 JShell 环境有额外开销
- 持久化:会话结束后代码丢失,虽可保存但不如正式项目管理
- 调试功能有限:复杂调试场景仍需 IDE
3. 私有接口方法
Java 8 引入了接口中的默认方法和静态方法,Java 9 更进一步,允许在接口中使用私有方法。
使用案例
java
public interface PaymentProcessor {
// 公共抽象方法
void processPayment(double amount);
// 默认方法
default void processExpress(double amount) {
log("开始快速支付处理");
validateAmount(amount); // 调用私有方法
processPayment(amount);
log("快速支付处理完成");
}
default void processStandard(double amount) {
log("开始标准支付处理");
validateAmount(amount); // 复用相同逻辑
processPayment(amount);
log("标准支付处理完成");
}
// 私有方法 - Java 9新特性
private void validateAmount(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("支付金额必须大于零");
}
if (amount > 100000) {
log("大额支付警告:" + amount);
}
}
// 私有静态方法 - Java 9新特性
private static void log(String message) {
System.out.println("支付日志: " + message);
}
}
私有接口方法的优势
- 代码复用:允许在默认方法之间共享代码,避免重复
- 更好的封装:实现细节可以保持私有,不暴露给实现类
- 更干净的接口:避免了辅助方法的公开暴露
- 减少实现类负担:无需在实现类中提供辅助功能
注意事项
- 只能在接口内部调用:私有方法不能被实现类或子接口访问
- 静态和非静态区别:私有静态方法可以被其他静态和非静态方法调用,但私有非静态方法不能被静态方法调用
4. 集合工厂方法
Java 9 之前,创建小型不可变集合很繁琐。Java 9 引入了便捷的工厂方法。
使用案例对比
Java 8 方式:
java
// 创建不可变List
List<String> list = Collections.unmodifiableList(Arrays.asList("Java", "Kotlin", "Scala"));
// 创建不可变Set
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("红", "绿", "蓝")));
// 创建不可变Map
Map<String, Integer> map = Collections.unmodifiableMap(new HashMap<String, Integer>() {{
put("一", 1);
put("二", 2);
put("三", 3);
}});
Java 9 方式:
java
// 创建不可变List
List<String> list = List.of("Java", "Kotlin", "Scala");
// 创建不可变Set
Set<String> set = Set.of("红", "绿", "蓝");
// 创建不可变Map
Map<String, Integer> map = Map.of(
"一", 1,
"二", 2,
"三", 3
);
// 更多键值对使用ofEntries方法
Map<String, Integer> largeMap = Map.ofEntries(
Map.entry("一", 1), // Map.entry是静态方法,创建Map.Entry实例
Map.entry("二", 2),
Map.entry("三", 3),
Map.entry("四", 4)
);
工厂方法的优势
- 代码简洁:显著减少了创建小型不可变集合的代码量
- 不可变保证:返回的集合不允许修改,尝试修改会抛出 UnsupportedOperationException
- 空值限制:不允许 null 元素,尝试添加 null 会抛出 NullPointerException
- 性能优化:专为小型集合优化,内存占用更少
局限性
- 不支持 null 元素:如果尝试创建包含 null 的集合(如 Set.of(null)),会立即抛出 NullPointerException
- 元素数量限制:直接的 of 方法最多支持 10 个元素(Map.of 最多支持 5 个键值对)
- 不可修改:如果需要后续修改集合,需使用其他构造方法
- 元素唯一性:Set.of 和 Map.of 会检查重复,重复元素会导致 IllegalArgumentException
5. Stream API 增强
Java 9 为 Stream API 添加了几个实用的新方法。
新方法使用案例
1. takeWhile() 和 dropWhile() - 短路操作
java
List<Integer> numbers = List.of(2, 4, 6, 8, 9, 10, 12);
// takeWhile:获取元素,直到条件不满足为止(短路操作)
List<Integer> evensUntilOdd = numbers.stream()
.takeWhile(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evensUntilOdd); // 输出: [2, 4, 6, 8]
// dropWhile:丢弃元素,直到条件不满足为止(短路操作)
List<Integer> afterFirstOdd = numbers.stream()
.dropWhile(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(afterFirstOdd); // 输出: [9, 10, 12]
// 对比filter(非短路,处理整个流)
List<Integer> allEvens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(allEvens); // 输出: [2, 4, 6, 8, 10, 12]
2. iterate 方法重载 - 有限流生成
java
// Java 8 的无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
// 需要额外使用limit限制
infiniteStream.limit(10).forEach(System.out::println);
// Java 9 新增的有限流 - 添加了停止条件
Stream<Integer> evenNumbersUntil100 = Stream.iterate(0, n -> n <= 100, n -> n + 2);
evenNumbersUntil100.forEach(System.out::println); // 打印0到100的偶数,自动停止
3. ofNullable 方法 - 安全处理可能为 null 的值
java
// 模拟可能返回null的方法
public static String getNameFromDatabase(int id) {
// 假设id=1时返回名字,其他情况返回null
return id == 1 ? "张三" : null;
}
// 处理可能为null的值 - Java 8方式
String name = getNameFromDatabase(2);
Stream<String> nameStream = name == null ? Stream.empty() : Stream.of(name);
// Java 9方式 - 更简洁
Stream<String> nameStream = Stream.ofNullable(getNameFromDatabase(2));
// 如果结果为null,返回空流而不是抛出异常
// 在处理可选数据时非常有用
List<String> names = List.of("user1", "user2", "user3");
List<String> validNames = names.stream()
.map(id -> getNameFromDatabase(Integer.parseInt(id.substring(4))))
.flatMap(Stream::ofNullable) // 过滤掉null值
.collect(Collectors.toList());
Stream 增强优势分析
- 更精确的控制:takeWhile 和 dropWhile 提供了更细粒度的流控制,实现短路处理
- 简化代码:特别是处理有限序列和可能为 null 的值时,代码更简洁
- null 安全:ofNullable 方法避免了 NullPointerException,简化空值处理
- 提高可读性:使流处理逻辑更直观,特别是有条件迭代时
- 性能优化:短路操作可以减少不必要的处理,提高效率
注意事项
- 顺序敏感:takeWhile 和 dropWhile 对元素顺序敏感,对无序流可能产生不可预测结果
- 有限流限制:iterate 的有限版本需要注意终止条件的设计,避免无限循环
- 语义区别:理解 takeWhile/dropWhile 与 filter 的本质区别(短路 vs 完整处理)
6. Optional 类增强
Optional 类在 Java 9 中获得了三个新方法,使其更加强大和灵活。
新方法使用案例
1. ifPresentOrElse() - 同时处理存在和不存在情况
java
Optional<String> optional = Optional.of("Java 9");
// 同时处理存在和不存在的情况 - Java 8方式
if (optional.isPresent()) {
System.out.println("值存在: " + optional.get());
} else {
System.out.println("值不存在");
}
// Java 9方式 - 更简洁
optional.ifPresentOrElse(
value -> System.out.println("值存在: " + value),
() -> System.out.println("值不存在")
);
2. or() - 提供备选 Optional
java
Optional<String> optional1 = Optional.empty();
Optional<String> optional2 = Optional.of("备选值");
// 如果optional1为空,则使用optional2 - Java 8方式
String result = optional1.isPresent() ? optional1.get() : optional2.orElse("默认值");
// Java 9方式 - 使用备选Optional
String result = optional1.or(() -> optional2).orElse("默认值");
System.out.println(result); // 输出: 备选值
// 链式处理多个可选数据源
String value = getUserInput()
.or(() -> getFromCache())
.or(() -> getFromDatabase())
.or(() -> getDefaultValue())
.orElse("最终默认值");
3. stream() - 将 Optional 转换为 Stream
java
Optional<String> optional = Optional.of("Java 9");
// 将Optional转换为Stream
Stream<String> stream = optional.stream();
stream.forEach(System.out::println); // 输出: Java 9
// 空Optional转换为空Stream
Optional<String> emptyOptional = Optional.empty();
long count = emptyOptional.stream().count(); // 结果为0
// 在过滤操作中特别有用
List<Optional<String>> listOfOptionals = Arrays.asList(
Optional.of("Java"),
Optional.empty(),
Optional.of("Kotlin")
);
// Java 8方式 - 繁琐
List<String> filteredList = listOfOptionals.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
// Java 9方式 - 简洁
List<String> filteredList = listOfOptionals.stream()
.flatMap(Optional::stream) // 只保留非空值
.collect(Collectors.toList());
System.out.println(filteredList); // 输出: [Java, Kotlin]
Optional 增强优势分析
- 更流畅的 API:新方法提供更连贯的编程体验,减少条件判断
- 更好的 Stream 集成:与 Stream API 的无缝集成,简化集合处理
- 减少样板代码:特别是在处理多个 Optional 对象时,代码更加简洁
- 函数式风格:强化了函数式编程模式,使代码更加声明式
- 更优雅的空值处理:提供多种处理缺失值的策略,代码更加健壮
注意事项
- 惰性求值:or()方法的 Supplier 参数仅在需要时才会被调用,有助于提高性能
- 性能考虑:链式调用过多可能导致性能开销,应适度使用
- 空 Optional 处理:理解 stream()方法对空 Optional 的处理(转换为空流)
7. HTTP/2 客户端
Java 9 引入了新的 HTTP 客户端 API,支持 HTTP/2 协议,替代了老旧的 HttpURLConnection。
使用案例
同步请求
java
// 创建HTTP客户端
HttpClient client = HttpClient.newHttpClient();
// 构建GET请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/users/octocat"))
.header("User-Agent", "Java 9 HttpClient Bot")
.GET() // GET是默认方法,可省略
.build();
// 同步发送请求
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 处理响应
System.out.println("状态码: " + response.statusCode());
System.out.println("响应体: " + response.body());
异步请求与错误处理
java
// 配置客户端连接池和超时
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.executor(Executors.newFixedThreadPool(5)) // 自定义线程池
.build();
// 异步请求
CompletableFuture<String> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenApply(body -> {
System.out.println("收到响应,长度: " + body.length());
return body;
})
.exceptionally(ex -> {
System.err.println("请求失败: " + ex.getMessage());
return "Error occurred";
});
// 处理完成或取消
try {
String result = future.get(5, TimeUnit.SECONDS); // 设置超时
System.out.println(result);
} catch (TimeoutException e) {
future.cancel(true); // 取消请求
System.err.println("请求超时,已取消");
} finally {
// 关闭自定义线程池
ExecutorService executor = (ExecutorService)client.executor().orElse(null);
if (executor != null) {
executor.shutdown();
}
}
POST 请求示例
java
// 构建POST请求
HttpRequest postRequest = HttpRequest.newBuilder()
.uri(URI.create("https://postman-echo.com/post"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"张三\",\"age\":30}"))
.build();
// 发送请求
HttpResponse<String> response = client.send(postRequest, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
新 HTTP 客户端的优势
- 支持 HTTP/2:更快的响应、头部压缩、服务器推送等特性
- 同步和异步 API:灵活适应不同场景,简化异步编程
- 流式请求和响应体:高效处理大型数据
- WebSocket 支持:内置的 WebSocket 实现
- 易用:比 HttpURLConnection 更直观的 API
- 请求/响应链接:响应对象包含对应的请求对象
局限性和注意事项
- 孵化器模块:在 Java 9 中为孵化器模块(即实验性质的 API,可能在后续版本中变更),需要特殊引入,Java 11 才正式成为标准模块
- 性能调优:对于高并发场景,需要适当配置连接池和线程策略
- 资源管理:大量异步请求需要注意资源管理和关闭
- 迁移成本:从 HttpURLConnection 迁移需要调整代码逻辑
8. 增强的 Try-With-Resources
Java 9 改进了 try-with-resources 语句,使其更加简洁。
使用案例对比
Java 7/8 方式:
java
// 资源需要在try-with-resources语句中声明
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// 如果资源在外部声明,需要重新声明
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
try (BufferedReader r = reader) { // 重复声明
String line = r.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
Java 9 方式:
java
// 外部声明的资源可以直接在try中使用
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
try (reader) { // 使用已声明的变量
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// 多个资源也可以简化
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
try (conn; stmt) { // 同时管理两个已声明的资源
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}
改进的优势
- 更简洁的代码:避免了资源变量的重复声明
- 提高可读性:特别是当多个资源需要关闭时
- 减少错误:减少了复制粘贴错误的可能性
- 灵活性:允许在声明和使用资源之间插入其他逻辑
限制条件
- final 或 effectively final:使用的变量必须是 final 或 effectively final
- AutoCloseable 要求:资源仍然必须实现 AutoCloseable 接口
- 变量作用域:使用的变量必须在 try 块结束后仍在作用域内
9. 改进的钻石操作符
Java 9 允许在匿名内部类中使用钻石操作符,这在 Java 8 中是不允许的。
使用案例对比
Java 8 方式:
java
// 必须明确指定泛型类型
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
};
// 在构造泛型实例的匿名子类时也必须重复类型参数
List<Integer> numbers = new ArrayList<Integer>() {
@Override
public boolean add(Integer e) {
System.out.println("添加元素: " + e);
return super.add(e);
}
};
Java 9 方式:
java
// 可以使用钻石操作符
Comparator<String> comparator = new Comparator<>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
};
// 匿名子类也可以使用钻石操作符
List<Integer> numbers = new ArrayList<>() {
@Override
public boolean add(Integer e) {
System.out.println("添加元素: " + e);
return super.add(e);
}
};
优势分析
- 代码简洁:减少类型重复声明,提高可读性
- 保持一致性:与局部变量类型推断的风格一致
- 减少错误:避免类型不匹配的错误,特别是在复杂泛型类型中
- 维护性:修改类型时只需修改一处,降低维护成本
注意事项
- 类型推断限制:某些复杂场景下编译器可能无法正确推断类型
- 向后兼容性:Java 9 代码在 Java 8 环境中可能无法编译
10. 进程 API 增强
Java 9 改进了对操作系统进程的控制和管理。
使用案例
java
// 获取当前进程
ProcessHandle current = ProcessHandle.current();
System.out.println("当前进程ID: " + current.pid());
// 获取进程信息
ProcessHandle.Info info = current.info();
System.out.println("命令: " + info.command().orElse("未知"));
System.out.println("启动时间: " + info.startInstant().orElse(null));
System.out.println("用户: " + info.user().orElse("未知"));
System.out.println("CPU使用时间: " + info.totalCpuDuration().orElse(null));
// 列出所有进程
ProcessHandle.allProcesses()
.filter(ph -> ph.info().command().isPresent())
.forEach(ph -> System.out.println(ph.pid() + ": " + ph.info().command().get()));
// 获取进程树
current.children().forEach(child ->
System.out.println("子进程: " + child.pid()));
current.descendants().forEach(desc ->
System.out.println("所有后代进程: " + desc.pid()));
// 启动新进程并等待终止
ProcessBuilder pb = new ProcessBuilder("notepad.exe");
Process process = pb.start();
ProcessHandle processHandle = process.toHandle();
// 异步监听进程终止
processHandle.onExit().thenAccept(ph ->
System.out.println("进程 " + ph.pid() + " 已终止,退出值: " +
process.exitValue()));
// 强制终止进程
boolean terminated = processHandle.destroy();
// 或强制终止
boolean forciblyTerminated = processHandle.destroyForcibly();
进程 API 增强优势
- 原生跨平台:无需使用平台特定代码,统一 API 适用所有平台
- 进程信息:更丰富的进程元数据,包括启动时间、命令行等
- 进程控制:更好的进程生命周期管理,包括优雅终止
- 异步通知:进程终止事件的异步处理,避免阻塞
- 进程树:轻松访问子进程、后代进程和父进程
局限性
- 权限限制:某些操作系统限制可能导致部分信息不可用
- 平台差异:虽然 API 统一,但某些功能在不同平台上行为可能不同
- 性能开销:频繁查询进程信息可能有性能开销
11. 多分辨率图像 API
Java 9 引入了处理多分辨率图像的 API,特别适合高 DPI 显示器。
使用案例
java
// 创建多分辨率图像
List<Image> images = new ArrayList<>();
images.add(ImageIO.read(new File("icon-16.png"))); // 16x16
images.add(ImageIO.read(new File("icon-32.png"))); // 32x32
images.add(ImageIO.read(new File("icon-64.png"))); // 64x64
// 创建多分辨率图像
MultiResolutionImage mrImage = new BaseMultiResolutionImage(images.toArray(new Image[0]));
// 获取特定分辨率的变体
Image variant = mrImage.getResolutionVariant(32, 32); // 获取最适合32x32显示的图像
// 获取所有变体
List<Image> variants = mrImage.getResolutionVariants();
// 假设有图形上下文
Graphics2D g2d = canvas.createGraphics();
// 在图形上下文中使用多分辨率图像 - 系统会自动选择最佳分辨率
g2d.drawImage(mrImage, x, y, null);
// 根据显示设备选择合适的变体
double deviceScale = 2.0; // 模拟Retina显示器
Image bestVariant = mrImage.getResolutionVariant(
16 * deviceScale, 16 * deviceScale); // 系统会选择32x32的变体
多分辨率图像 API 优势
- 高 DPI 适配:在不同 DPI 屏幕上优化显示效果,特别是 HiDPI 显示器
- 自动选择:系统根据显示需求选择最佳分辨率变体
- 兼容性:与现有图像 API 无缝集成
- 简化开发:减轻开发者处理多分辨率屏幕的负担
- 平台一致性:跨平台提供一致的高质量显示
限制和注意事项
- 内存消耗:存储多个分辨率变体需要更多内存
- 性能考虑:处理大量多分辨率图像可能影响性能
- 变体匹配算法:了解系统如何选择最佳变体,确保提供合适的分辨率集
12. 其他值得关注的改进
1. Reactive Streams API
Java 9 引入了java.util.concurrent.Flow
类,提供了响应式编程的标准 API。
java
// 发布者
class MyPublisher implements Flow.Publisher<String> {
private List<Flow.Subscriber<? super String>> subscribers = new ArrayList<>();
@Override
public void subscribe(Flow.Subscriber<? super String> subscriber) {
subscribers.add(subscriber);
subscriber.onSubscribe(new Flow.Subscription() {
@Override
public void request(long n) {
// 发送数据
}
@Override
public void cancel() {
subscribers.remove(subscriber);
}
});
}
public void publish(String item) {
subscribers.forEach(s -> s.onNext(item));
}
}
// 订阅者
class MySubscriber implements Flow.Subscriber<String> {
private Flow.Subscription subscription;
@Override
public void onSubscribe(Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1); // 请求一个元素
}
@Override
public void onNext(String item) {
System.out.println("接收: " + item);
subscription.request(1); // 请求下一个元素
}
@Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("完成");
}
}
与 Spring Reactor 结合
java
// 使用Reactor库实现响应式流
import reactor.core.publisher.Flux;
// 创建Flux发布者
Flux<String> messages = Flux.just("消息1", "消息2", "消息3")
.concatWith(Flux.error(new RuntimeException("模拟错误")))
.concatWith(Flux.just("消息4"))
.onErrorReturn("错误后的备用消息");
// 订阅处理
messages.subscribe(
msg -> System.out.println("接收: " + msg),
error -> System.err.println("错误: " + error),
() -> System.out.println("完成")
);
2. CompletableFuture API 改进
Java 9 为 CompletableFuture 添加了超时、延迟和其他增强功能。
java
// 添加超时
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000); // 模拟长时间运行
return "结果";
} catch (InterruptedException e) {
return "interrupted";
}
});
// 设置超时 - 1秒后超时并返回默认值
String result = future.completeOnTimeout("超时默认值", 1, TimeUnit.SECONDS)
.join();
System.out.println(result); // 输出: 超时默认值
// 延迟执行
CompletableFuture<String> delayed = CompletableFuture
.supplyAsync(() -> "延迟结果")
.orTimeout(5, TimeUnit.SECONDS) // 5秒超时,抛出异常
.completeOnTimeout("超时值", 3, TimeUnit.SECONDS); // 3秒后返回默认值
3. Stack-Walking API
新的 Stack-Walking API 提供了更高效的堆栈遍历方式。
java
// 获取调用栈并过滤
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
// 查找特定类的调用者
Optional<Class<?>> callerClass = walker.walk(frames ->
frames.filter(f -> !f.getClassName().equals(MyClass.class.getName()))
.findFirst()
.map(StackWalker.StackFrame::getDeclaringClass));
// 打印整个调用栈
walker.forEach(frame -> System.out.println(frame.getClassName() + "." +
frame.getMethodName() + " (行: " + frame.getLineNumber() + ")"));
// 收集特定深度的调用栈
List<String> callStack = walker.walk(frames ->
frames.limit(5) // 只获取前5个帧
.map(frame -> frame.getClassName() + "." + frame.getMethodName())
.collect(Collectors.toList()));
4. 增强的 Deprecation
@Deprecated 注解增加了 forRemoval 和 since 属性。
java
// Java 8方式
@Deprecated
public void oldMethod() {
// 实现
}
// Java 9方式 - 更丰富的信息
@Deprecated(since = "9", forRemoval = true)
public void veryOldMethod() {
// 这个方法将在未来版本中删除
}
@Deprecated(since = "9", forRemoval = false)
public void legacyMethod() {
// 这个方法不推荐使用,但暂时不会删除
}
总结
以下是 Java 9 主要新特性的总结表格:
特性 | 核心优势 | 应用场景 | 注意事项 |
---|---|---|---|
模块系统 | 强封装性、明确依赖、运行时优化 | 大型应用、库开发、微服务 | 遗留代码兼容性问题、反射限制 |
JShell | 快速测试、即时反馈、原型设计 | 学习、原型开发、API 探索 | 不适用于性能测试、有限的调试 |
私有接口方法 | 代码复用、更好封装 | 接口设计、API 开发 | 仅接口内部可见、静态方法限制 |
集合工厂方法 | 代码简洁、不可变保证 | 配置数据、常量集合 | 不支持 null(抛出 NullPointerException)、元素数量限制 |
Stream API 增强 | 短路操作、简化代码、null 安全 | 数据处理、函数式编程 | 顺序敏感、语义差异 |
Optional 增强 | 流畅 API、更好集成、函数式风格 | 空值处理、函数式编程 | 惰性求值、性能考虑 |
HTTP/2 客户端 | HTTP/2 支持、异步 API、WebSocket | Web 服务集成、API 调用 | 孵化器模块、资源管理 |
改进的 Try-With-Resources | 更简洁代码、提高可读性 | 资源管理、IO 操作 | 变量必须为 final 或 effectively final |
改进的钻石操作符 | 代码简洁、类型安全 | 泛型编程、集合操作 | 复杂场景推断限制 |
进程 API 增强 | 原生跨平台、丰富进程信息 | 系统监控、进程管理 | 权限限制、平台差异 |
多分辨率图像 API | 高 DPI 适配、自动选择 | GUI 开发、桌面应用 | 内存消耗、变体选择算法 |
Java 9 虽然没有 Java 8 那样的语法革命,但它通过模块系统和 API 改进为 Java 生态系统带来了基础架构级别的升级。这些变革为 Java 平台的长期发展奠定了基础,特别是在微服务、容器化和云原生应用开发方面。
模块系统解决了"JAR 地狱"问题,提高了平台安全性,也为 JDK 本身的演进提供了更灵活的路径。这为后续 Java 11、Java 17 等 LTS 版本中的重大改进铺平了道路。模块系统的引入使得 Java 平台能够实现更精细的功能拆分,在 Java 11 中实现了模块化 JRE,大幅减小了运行时的体积,为容器化部署和微服务架构提供了更好的支持。
迁移建议
对于想要利用 Java 9 模块系统的企业开发者,建议采用以下渐进式迁移策略:
- 从自动模块开始:首先将现有 JAR 放在模块路径上,让它们成为自动模块,不需要 module-info.java
- 增量添加模块描述:为每个组件逐步添加模块描述文件,优先从底层库开始
- 重构内部依赖:基于"高内聚、低耦合"原则重新设计模块边界
- 利用 JPMS 工具:使用 jdeps 等工具分析依赖关系,帮助确定模块结构
- 应用封装特性:逐步应用强封装,利用 exports 和 opens 限制不必要的 API 暴露
这种渐进式迁移可以最小化风险,同时逐步获得模块系统的好处。
感谢您耐心阅读到这里!如果觉得本文对您有帮助,欢迎点赞 👍、收藏 ⭐、分享给需要的朋友,您的支持是我持续输出技术干货的最大动力!
如果想获取更多 Java 技术深度解析,欢迎点击头像关注我,后续会每日更新高质量技术文章,陪您一起进阶成长~