让 Java 8 改进我们的程序
作者:互联网
这些文字写的很迟,因为 Java 8 已经发布六年有余。
本文不是 Java 语法或 Java 8 新特性教程,故不涉及系统、细致的用法介绍而是只包含 Java 8 对旧程序的改进措施。
摘要
本文叙述了 Java 8 提供的新特性对现有代码的改进方式以及带来的好处。主要涉及比较重要的新特性:Optional、接口的默认方法、函数式、stream、CompletableFuture。
改进内容
Optional,更好的判别 null 的方式
Optional.ofNullable 可以用来构造一个可能为 null 的 Optional 对象
public void myFunc(Tea tea) {
Optional.ofNullable(tea).map(Tea::getName).orElse("锡兰红茶");
// ......
}
值得注意的是,一般情况下我们不应该使用它的 isPresent 或 isEmpty 方法,不然就和以前的 if (a == null) 方式一样了。
真正应该被关注的方法有:
- orElse 或 orElseGet
- filter
- map
- flatMap
这些方法灵活配合,可以实现嵌套判空、链式处理,又省事又不容易出错。
给接口添加默认方法(default method)简化代码
在 Java 8 中,我们可以给结构添加默认的方法,如此一来,就不必让每个实现类都重写(override)一下这个方法。
最简单的例子就是 JDK 中的 Iterable
另一个例子是数据库连接池 Druid,它在 1.2 版本开始用默认方法简化了很多代码。
值得注意的问题是接口的多继承(多实现)问题,本来,Java 不允许类多继承,避免了 C++ 中菱形继承的问题,默认方法的特性又把这个问题带回了 Java。解决办法是显示重写方法。
interface A {
default void foo() {
System.out.println("A::foo");
}
}
interface B {
default void foo() {
System.out.println("B::foo");
}
}
class C implements A, B {
// C 必须重写 foo
// 不然 Java 编译器不知道咋弄
@Override
public void foo() {
System.out.println("C::foo");
}
}
public class App {
public static void main(String[] args) {
A a = new C();
a.foo(); // C::foo
}
}
在恰当的时候使用 lambda 表达式与流式操作
一些临时函数应该用 lambda 表达式来代替。与旧式写法相比,少了污染视线的工具类(碍事)。
在“对一组对象进行一系列工序”的场景下,相比于旧式写法,用流式操作可以使程序简洁、更可读、更不易出错:
例 1:把用逗号分隔的多个职位(positions)中的每个职位转换到数字,然后取最大值。
final String[] ps = positions.split(",");
Arrays.stream(ps)
.map(PositionToNumber::calc) // calc 是一个把 String 转换成 long 的函数
.max(Long::compareTo)
.orElse(Long.MIN_VALUE);
例 2:把所有雇员按照职位级别排倒序。
employeeList
.stream()
.sorted(Comparator.comparing(Employee::getCachePositionLevel).reversed())
.collect(Collectors.toList());
永远使用新的日期时间 API
java.time.* 中的类(LocalDate,LocalTime,LocalDateTime 等)都是不可变的,所以完全不用担心并发安全问题。时间之间的加减都会返回新对象而非修改已有对象。格式也统一为 ISO-8601,不再有乱七八糟的各种格式。
of、with 系列的方法直观好用。星期几、月份也改成了枚举。一系列细节上的优点令人舒坦。
JDBC 也规定了数据库中日期时间相关类型也 Java 中日期时间类型的对应关系:
SQL | Java |
---|---|
DATE | LocalDate |
TIME | LocalTime |
DATETIME | LocalDateTime |
TIMESTAMP | LocalDateTime |
并发工具类 StampedLock 的合理运用
在读多写少的时候,StampedLock 比 ReadWriteLock 性能更好,但它不支持重入,不支持条件变量。
另外,在 StampedLock 在上锁后切勿调用线程的 interrupt,不然 CPU 使用率会立刻上去。
灵活选用 CompletableFuture 和 RxJava 实现异步
在 Java 中表示异步操作的传统方式和大多数编程语言一样:回调函数(方法)。但一旦这么做,代码会变得和乱柴火垛似的,维护代价极大,还容易藏着一些隐晦的 BUG。
Java 8 为我们提供了新方法。
在异步场景不过于复杂时,CompletableFuture 是极好的异步操作工具。
CompletableFuture 的静态方法 runAsync 和 supplyAsync 用来将一个操作异步化。supplyAsync 可以指定线程池,若不指定,则使用一个公共的线程池(CompletableFuture 类中的一个静态私有变量(Executor))。
supplyAsync(supplier);
supplyAsync(supplier, executor);
应该根据实际场景,用合适的方式使用自己创建的线程池。例如,在 HTTP 客户端开发中,每个 Client 中应该包含一个线程池,避免过多请求拥挤在一个线程池中。
CompletableFuture 实现了 CompletionStage,而 CompletionStage 提供了 then 系列方法(如 thenCombine,thenCombine)可以将操作组成流程图,且支持条件执行和关系汇聚。
RxJava 不是 Java 8 才有的(Java 6 即可支持),功能极为强大。本文不是 RxJava 教程,此处从略。
根据使用场景,灵活选用二者。让 Java 异步编程轻松搞定。
结束语
上面介绍了 Java 8 主要新特性如何改善程序,其实 Java 8 还有很多新的细节值得进一步挖掘。现在 Java 11 早已发布,Java 17 也已经不远,新时代的 Java 不仅提供了便利的特性和工具,也能更好地适应现代地基础架构(如轻量级开发、容器化、Serverless),让产品持续焕发活力。
标签:异步,Java,程序,改进,CompletableFuture,线程,foo,方法 来源: https://www.cnblogs.com/jthmath/p/16503454.html