JAVA8新特性介绍

简介: JAVA8新特性介绍 # 特性介绍 ## Lambda表达式(函数式编程) lambda 表达式让你用一种简洁的方式去避免一大块的代码。例如,你需要一个线程来执行一个任务。需要创建一个 Runnable 对象,然后做为参数传递给 Thread。 ```java new Thread(new Runnable() { @Override public v

JAVA8新特性介绍

特性介绍

Lambda表达式(函数式编程)

lambda 表达式让你用一种简洁的方式去避免一大块的代码。例如,你需要一个线程来执行一个任务。需要创建一个 Runnable 对象,然后做为参数传递给 Thread。

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World");
    }
}).start();

使用lambda表达式的话,可以简化很多

new Thread(() -> System.out.println("Hello World")).start();

方法引用

方法引用可以理解为一个特殊的 lambda 表达式,也是一个新的特性。它可以快速的选择定义在类中的已经存在的方法。例如:输出一个字符串,通常可以这样写(JAVA8):

Arrays.asList(1, 2, 3, 4, 5).forEach(new Consumer<Integer>() {
    @Override
    public void accept(Integer integer) {
        System.out.println(integer);
    }
});

使用lambda表达式的话:

Arrays.asList(1, 2, 3, 4, 5).forEach(integer -> System.out.println(integer));

改用方法引用会更加简洁:

Arrays.asList(1, 2, 3, 4, 5).forEach(System.out::println);

符号::是方法引用时特有的。

Streams

几乎每一个 JAVA 应用程序都会创建和处理集合。它们是许多程序处理任务的基石,集合可以用来聚合及处理数据。然而,处理集合过于繁琐而且难以处理并发。比如,从一个发票列表中找到“聚餐”相关,且金额在200元以上的发票ID,并按按发票金额的数值排序:

List<Invoice> invoices = new ArrayList<>();
List<Invoice> selectedInvoices = new ArrayList<>();
for (Invoice inv : invoices) {
    if (inv.getTitle().contains("聚餐") && inv.getAmount() > 200) {
        selectedInvoices.add(inv);
    }
}
Collections.sort(selectedInvoices, new Comparator<Invoice>() {
    public int compare(Invoice inv1, Invoice inv2) {
        // 倒序
        return inv2.getAmount().compareTo(inv1.getAmount());
    }
});
List<Integer> selectedInvoicesIds = new ArrayList<>();
for (Invoice inv : selectedInvoices) {
    selectedInvoicesIds.add(inv.getId());
}

上面冗长的判断,是计算机应该执行的命令。而使用JAVA8中引入的流来处理的话,则不太一样了,可以很直观的看到每一步做的处理。

List<Invoice> invoices = new ArrayList<>();
List<Integer> selectedInvoicesIds = invoices.stream()
        .filter(inv -> inv.getTitle().contains("聚餐") && inv.getAmount() > 200)
        .sorted(Comparator.comparingDouble(Invoice::getAmount).reversed())
        .map(Invoice::getId)
        .collect(Collectors.toList());

甚至在Streams中,可以简单的通过将invoices.stream()替换成invoices.parallelStream()就实现了集合的并发处理。当然,并不是所有情况都适合用并发处理。

另外,上面没有考虑空指针的情况,请看Optional

Optional

JAVA8 中引入了一个新的类叫做 Optional。很多编程语言都可以自动处理空值,而JAVA没有,这就导致了空指针异常这个几乎每个人都遇到过,也深感邪恶的异常。Optional灵感来自于函数式编程语言,它的引入是为了当值为空时代码可以争取地执行。

Optional是一种单值容器,这种情况下如果没有值则为空。Optional 很久以前已经在第三方集合框架(比如 Guava)中可用,但现在它作为 JAVA API 的一部分,可用于JAVA中。事实上,Optional 定义了方法强制你去明确地检查值存在还是缺省。

上述代码中,inv.getTitle()inv.getAmount()都没有判断空,这显然在很有可能的情况下导致空指针异常,修改如下:

List<Invoice> invoices = new ArrayList<>();
List<Integer> selectedInvoicesIds = invoices.stream()
        .filter(inv -> Optional.ofNullable(inv.getTitle()).orElse("").contains("聚餐") && Optional.ofNullable(inv.getAmount()).orElse(0d) > 200)
        .sorted(Comparator.comparingDouble((ToDoubleFunction<Invoice>) item -> Optional.ofNullable(item.getAmount()).orElse(0d)).reversed())
        .map(Invoice::getId)
        .collect(Collectors.toList());

显得有一些杂乱,但是比起写很多if else判断来避免空指针,这种方式显然更加方便。

简单示例
再看一个简单的例子,如果有一个门店信息,要获取它所在的省份和城市:

City city = store.getProvince().getCity()

如果省份没有设置,那不可避免会带来空指针异常,但是如果需要判断的话,你得像下面这样:

City city = null;
if (store != null) {
    Province province = store.getProvince();
    if (province != null) {
        city = province.getCity();
    }
}

而使用 Optional 包一层的话,简单,且可以完全避免异常:

City city = Optional.ofNullable(store).map(Store::getProvince).map(Province::getCity).orElse(null);

Optional 的map方法会判断值是否为空,所以可以很好的避免空带来的异常。

CompletableFuture

在并发编程中,我们通常会用到一组非阻塞的模型:Promise,Future 和 Callback。其中的 Future 表示一个可能还没有实际完成的异步任务的结果,针对这个结果可以添加 Callback 以便在任务执行成功或失败后做出对应的操作,而 Promise 交由任务执行者,任务执行者通过 Promise 可以标记任务完成或者失败。 可以说这一套模型是很多异步非阻塞架构的基础。

这一套经典的模型在 Scala、C# 中得到了原生的支持,但 JAVA8 之前并没有支持 Callback 的 Future 可用,当然也并非在 JAVA 界就没有发展了,比如 Guava 就提供了ListenableFuture 接口,而 Netty 4+ 更是提供了完整的 Promise、Future 和 Listener 机制。

JAVA8中提供 CompletableFuture 作为支持回调的 Future,在异步场景下,不再需要手动调用future.get()来等待结果,只需要提供complete,exceptionally,thenApply,thenAccept,thenRun就可以使用回调机制,轻松处理“完成后回调”、“异常时回调”、“继续执行”、“接受结果”、“继续运行(此处多为依赖的异步调用)”。

CompletableFuture 可以使用的场景足以独立成文,而且文章不少呢,不再赘述。CompletableFuture 在异步/回调上和 RxJava 是类似的,但是 RxJava 可以处理的场景更加丰富。

接口default方法

JAVA8 中对接口进行了两大改造,使其可以在接口中声明具体的方法。

引入默认方法

它可以在接口声明的方法中增加实现体,作为一种将 JAVA API 演变为向后兼容的机制。例如,可以看到在 JAVA8 的 List 接口中现在支持一种排序方法,像下面这么定义的:

default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}

接口现在也可以拥有静态方法

它和定义一个接口,同时用一个内部类定义一个静态方法去进行接口的实例化是同一种机制。例如,JAVA 中有 Collection 接口和定义了通用静态方法的 Collections 类,现在这些通用的静态也可以放在接口中。例如,JAVA8 中的 stream 接口是这样定义静态方法的:

public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

新的Date和Time接口(类似JodaTime)

JAVA8 引入了一套新的日期时间 API ,修复了之前旧的 Date 和 Calendar 类的许多问题。这一套新的Date和Time的时间API风格,基本和Joda-Time一致,毕竟Joda-Time框架的作者正是JSR-310的规范的倡导者。

这套新的日期时间 API 包含两大主要原则:

领域驱动设计

新的日期时间 API 采用新的类来精确地表达多种日期和时间的概念。例如,可以用 Period 类去表达一个类似于 “2个月零3天(63天)”,用 ZonedDateTime 去表达一个带有时间区域的时间。每一个类提供特定领域的方法且采用流式风格。因此,可以通过方法链写出可读性更强的代码。

不变性

Date(日期) 和 Calendar(日历)的其中一个问题就是他们是非线程安全的。此外,当使用 Date 作为API的一部分时,Date 的值可能会被意外的改变。为了避免这种潜在的BUG,在新的日期时间 API 中的所有类都是不可变的。

也就是说,在新的日期时间 API 中,你不能改变对象的状态,取而代之的是,你调用一个方法会返回一个带有更新的值的新对象。

带来的好处

代码可读性

JAVA 写出来的代码,大多是比较繁琐的,这导致了可读性的降低。换句话说,它需要很多代码才能表达一个简单的概念。

举个例子:简单的递减排序

List<Integer> integers = Arrays.asList(1, 34, 771, 14, 3, 5, 299);
Collections.sort(integers, new Comparator<Integer>() {
    public int compare(Integer a, Integer b) {
        return Integer.compare(b, a);
    }
});
System.out.println(integers);

你知道什么时候是a-b,什么时候是b-a吗?

排序的时候,要关注具体哪个值减哪个值,学 C 语言的时候就知道的,根据返回值是大于零、等于零、小于零来判断。但是可读性差,而且每次排序都要关注这个细节,非常繁琐。

而使用JAVA8 的流、以及新的接口方法,可以简单的排序,并且可以不关注排序细节。

List<Integer> integers = Arrays.asList(1, 34, 771, 14, 3, 5, 299);
integers.sort(Comparator.comparingInt(Integer::intValue).reversed());
System.out.println(integers);

提升多核心处理能力

这里的最佳体现就是并行流。我们平时的代码中有大量的集合处理,而通常情况下,都是串行处理的,毕竟写一个并行处理集合的程序太过于复杂了,提升的速度可能不及带来BUG的风险。并行流的存在,非常简单的解决了这个问题。

当然,还有CompletableFuture,它的出现也是为了能够在更多需要使用异步编程的时候,可以简单地实现,而不是写出可能带有一堆BUG的异步程序。

总结

总之,一切都在向好的方向发展,更专业的人做更专业的事。复杂的并行、异步,交给更深入底层的人来做。我们做好业务上需要的并发和异步。

函数式的编程,带来了非常强的代码可读性,又可以避免空指针异常,非常适合在流程性业务逻辑中使用起来。易读的代码,也会给日后的维护带来遍历。

我建议,大家都用JAVA8,将这些新特性都用上,很明显,它们比之前的版本好太多了。

参考文献:

译文《JAVA8开发指南》为什么你需要关注 JAVA8

原文《JAVA8开发指南》

并发编程 Promise, Future 和 Callback

目录
相关文章
|
19天前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
47 2
|
21天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
34 3
|
21天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
31 2
|
23天前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
27 3
|
26天前
|
安全 Java API
Java 17新特性让你的代码起飞!
【10月更文挑战第4天】自Java 8发布以来,Java语言经历了多次重大更新,每一次都引入了令人兴奋的新特性,极大地提升了开发效率和代码质量。本文将带你从Java 8一路走到Java 17,探索那些能让你的代码起飞的关键特性。
71 1
|
1月前
|
编解码 Oracle Java
java9到java17的新特性学习--github新项目
本文宣布了一个名为"JavaLearnNote"的新GitHub项目,该项目旨在帮助Java开发者深入理解和掌握从Java 9到Java 17的每个版本的关键新特性,并通过实战演示、社区支持和持续更新来促进学习。
70 3
|
3天前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
12 4
|
17天前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
39 3
|
17天前
|
存储 安全 Java
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
23 2
|
24天前
|
Java 开发者
在Java的集合世界里,Set以其独特的特性脱颖而出,它通过“哈希魔法”和“红黑树防御”两大绝技
【10月更文挑战第13天】在Java的集合世界里,Set以其独特的特性脱颖而出。它通过“哈希魔法”和“红黑树防御”两大绝技,有效抵御重复元素的侵扰,确保集合的纯洁性和有序性。无论是“人海战术”还是“偷梁换柱”,Set都能从容应对,成为开发者手中不可或缺的利器。
30 6
下一篇
无影云桌面