这篇最全 Java 8 讲解,有没有之一先看后说
作者头像
  • 2020-08-18 13:58:22 3

一、基础知识

1)为什么要学习 Java 8

Java 8 的革新在许多方面超越了以往版本的更新,这些改动使编程变得更加便捷。

传统写法java List<Person> personList = Arrays.asList(new Person(21,50),new Person(22,55),new Person(23,60)); Collections.sort(personList, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getWeight().compareTo(o2.getWeight()); } });

Java 8 写法java personList.sort(Comparator.comparing(Person::getWeight));

熟悉 Linux 操作的同学应该知道这个命令

bash cat testFile | tr "[A-Z]" "[a-z]" | sort | tail -3

这种操作类似于流水线,通过逐个处理每个步骤的输出,最后得到结果。

函数传递

假设有一个花的列表: java List<Flower> flowerList = Arrays.asList(new Flower("red", 6), new Flower("yellow", 7), new Flower("pink", 8));

传统写法java List<Flower> resList = new ArrayList<>(); for (Flower flower : flowerList) { if (StringUtils.equals("red", flower.getColor())) { resList.add(flower); } }

另一种需求java List<Flower> resList = new ArrayList<>(); for (Flower flower : flowerList) { if (flower.getPrice() < 8) { resList.add(flower); } }

我们可以通过抽取条件方法来简化代码: ```java public static boolean isRed(Flower flower) { return StringUtils.equals("red", flower.getColor()); }

public static boolean isLowPrice(Flower flower) { return flower.getPrice() < 8; } ```

利用 Predicate 函数式接口,可以将自定义的方法传递出来: java public static List<Flower> filterFlower(List<Flower> flowers, Predicate<Flower> p) { List<Flower> resList = new ArrayList<>(); for (Flower flower : flowers) { if (p.test(flower)) { resList.add(flower); } } return resList; }

使用java filterFlower(flowerList, Flower::isRed); filterFlower(flowerList, Flower::isLowPrice);

通过 Lambda 表达式,可以进一步简化: java filterFlower(flowerList, (Flower f) -> StringUtils.equals("red", f.getColor())); filterFlower(flowerList, (Flower f) -> f.getPrice() < 8);

默许方法

在 Java 8 之前,如果新增接口方法,所有实现该接口的类都需要重写该方法,否则会编译错误。Java 8 引入了默认方法,解决了这个问题。

```java interface Animal { void eat(); default void fly() { System.out.println("animal fly"); } }

class Bird implements Animal { @Override public void eat() {} @Override public void fly() { System.out.println("bird fly"); } }

class Dog implements Animal { @Override public void eat() {} } ```

2)行为参数化

开发过程中,我们需要适应不断变化的需求,一种有效的方式是行为参数化。

需求1:筛选红色的花 java public static List<Flower> filterFlower(List<Flower> flowers) { List<Flower> resList = new ArrayList<>(); for (Flower flower : flowers) { if (StringUtils.equals("red", flower.getColor())) { resList.add(flower); } } return resList; }

需求2:筛选绿色的花

通过传递颜色参数,可以动态筛选: java public static List<Flower> filterFlowerByColor(List<Flower> flowers, String color) { List<Flower> resList = new ArrayList<>(); for (Flower flower : flowers) { if (StringUtils.equals(color, flower.getColor())) { resList.add(flower); } } return resList; }

需求3:筛选价格低于8元的花

为了适应价格变化,可以将价格设为可变参数: java public static List<Flower> filterFlowerByPrice(List<Flower> flowers, Integer price) { List<Flower> resList = new ArrayList<>(); for (Flower flower : flowers) { if (flower.getPrice() < price) { resList.add(flower); } } return resList; }

为了代码整洁,可以将这些逻辑封装在一个方法中: java public static List<Flower> filterFlower(List<Flower> flowers, String color, Integer price, Boolean flag) { List<Flower> resList = new ArrayList<>(); for (Flower flower : flowers) { if ((flag && flower.getPrice() < price) || (!flag && StringUtils.equals(color, flower.getColor()))) { resList.add(flower); } } return resList; }

使用标志 flag 来控制筛选条件,但这种方法不够优雅。我们可以将筛选行为作为参数传递:

定义一个过滤行为的接口: java interface FilterPredicate { boolean test(Flower flower); }

自定义两个行为过滤类: ```java class RedColorFilterPredicate implements FilterPredicate { @Override public boolean test(Flower flower) { return StringUtils.equals("red", flower.getColor()); } }

class LowPriceFilterPredicate implements FilterPredicate { @Override public boolean test(Flower flower) { return flower.getPrice() < 8; } } ```

重写筛选方法,通过传递行为参数: java public static List<Flower> filterFlower(List<Flower> flowers, FilterPredicate filter) { List<Flower> resList = new ArrayList<>(); for (Flower flower : flowers) { if (filter.test(flower)) { resList.add(flower); } } return resList; }

使用java filterFlower(flowerList, new RedColorFilterPredicate()); filterFlower(flowerList, new LowPriceFilterPredicate());

我们还可以直接使用 Lambda 表达式传递行为: java filterFlower(flowerList, (Flower flower) -> flower.getPrice() > 8); filterFlower(flowerList, (Flower flower) -> flower.getPrice() > 8 && StringUtils.equals("red", flower.getColor()));

这种方式可以使代码更加简洁和灵活。

3)初识 Lambda

Lambda 表达式是一种简化的匿名函数表示方式,它没有名称,但有参数列表、函数体和返回类型。

例子

  • (String s) -> s.length():从对象中抽取值,具有一个 String 类型的参数,返回一个 int 类型的值。
  • (Flower f) -> f.getPrice() > 8:布尔表达式,具有一个 Flower 类型的参数,返回一个 boolean 类型的值。
  • (String s) -> {System.out.print(s);}:生成一个对象,具有一个 String 类型的参数,无返回值。
  • () -> new Flower("red", 8):创建一个对象,无传入参数,返回一个 int 类型的值(1)。

函数式接口

函数式接口是指只定义一个抽象方法的接口,并使用 @FunctionalInterface 注解标记。

例子: ```java @FunctionalInterface public interface Comparator { int compare(T o1, T o2); }

@FunctionalInterface public interface Runnable { void run(); }

@FunctionalInterface public interface ActionListener extends EventListener { void actionPerformed(ActionEvent e); }

@FunctionalInterface public interface Callable { V call(); } ```

Lambda 表达式可以直接为函数式接口的抽象方法提供实现,并将整个表达式作为函数式接口的实例。

java Runnable runnable = () -> System.out.println("这是运用 Lambda 的写法");

使用函数式接口

Predicate

这个接口定义了一个 test() 的抽象方法,它接受泛型 T 对象并返回一个 boolean 类型的值。当你需要表示一个涉及类型 T 的布尔表达式时,可以使用这个接口。

```java public static List filterFlower(List flowers, Predicate p) { List resList = new ArrayList<>(); for (Flower flower : flowers) { if (p.test(flower)) { resList.add(flower); } } return resList; }

filterFlower(flowerList, (Flower flower) -> flower.getPrice() > 8); ```

Consumer

这个接口定义了一个 accept() 的抽象方法,它接受泛型 T 对象,没有返回值。当你需要访问类型 T 的对象并对它执行某些操作时,可以使用这个接口。

java List<Integer> nums = Arrays.asList(1, 2, 3, 4); nums.forEach(integer -> System.out.println(integer));

Function

这个接口定义了一个 apply() 的抽象方法,它接受泛型 T 对象并返回一个泛型 R 的对象。当你需要定义一个 Lambda 将输入对象的信息映射到输出时,可以使用这个接口。

java (String s) -> s.length()

Supplier

这个接口定义了一个 get() 的抽象方法,它没有传入参数,并返回一个泛型 T 的对象。如果你需要定义一个 Lambda 输入自定义的对象,可以使用这个接口。

java Callable<Integer> call = () -> 1;

类型检查和类型推断

以这个为例:

java filter(flowerList, (Flower flower) -> flower.getPrice() > 8);

首先找出 filter 方法的声明要求第二个参数是 Predicate 类型的对象。Predicate 是一个函数式接口,定义了一个 test() 的抽象方法,并返回一个 boolean 类型的值。

我们可以继续简化代码:

java filterFlower(flowerList, f -> f.getPrice() > 8);

部分变量的使用

Lambda 表达式不仅可以使用主体内部的参数,还可以使用自由变量(在外部作用域中定义的变量)。

java int tmpNum = 1; Runnable r = () -> System.out.println(tmpNum);

注意:Lambda 表达式对于全局变量和静态变量可以无限制地使用,但对于局部变量必须显式声明为 final 或实际上是 final 的变量。

方法引用

方法引用允许你根据已有方法的实现来创建 Lambda 表达式,可以看作是单一方法的 Lambda 表达式的语法糖。

例子

```java List flowerList = Arrays.asList(new Flower("red", 6), new Flower("yellow", 7), new Flower("pink", 8));

(Flower f) -> f.getPrice(); // ==> Flower::getPrice

flowerList.stream().map(t -> t.getPrice()).collect(Collectors.toList()); // ==> flowerList.stream().map(Flower::getPrice).collect(Collectors.toList()); ```

复合 Lambda 表达式

比较器复合

我们有一组鲜花集合如下:

java List<Flower> flowerList = Arrays.asList(new Flower("red", 6), new Flower("yellow", 7), new Flower("pink", 8), new Flower("white", 8));

按鲜花的价格排序:

java flowerList.sort(Comparator.comparing(Flower::getPrice));

默认是升序排列,若想降序排列:

java flowerList.sort(Comparator.comparing(Flower::getPrice).reversed());

若想在价格排序后按颜色排序:

java flowerList.sort(Comparator.comparing(Flower::getPrice).thenComparing(Flower::getColor));

谓词复合

用于 Predicate 接口:

  • negate:非
  • and:且
  • or:或

```java Predicate redFlower = (t) -> StringUtils.equals("red", t.getColor()); Predicate notRedFlower = redFlower.negate();

Predicate redFlower = (t) -> StringUtils.equals("red", t.getColor()); Predicate lowPriceFlower = (t) -> t.getPrice() < 8; Predicate redAndLowPriceFlower = redFlower.and(lowPriceFlower);

Predicate redFlower = (t) -> StringUtils.equals("red", t.getColor()); Predicate lowPriceFlower = (t) -> t.getPrice() < 8; Predicate redOrLowPriceFlower = redFlower.or(lowPriceFlower); ```

函数复合

用于 Function 接口:

  • andThen
  • compose

```java Function addRes = a1 -> a1 + 1; Function mulRes = a1 -> a1 * 2; Function andThenResult = addRes.andThen(mulRes); Integer apply = andThenResult.apply(1); // 结果为 4 ==> (1 + 1) * 2

Function addRes = a1 -> a1 + 1; Function mulRes = a1 -> a1 * 2; Function composeResult = addRes.compose(mulRes); Integer apply = composeResult.apply(1); // 结果为 3 ==> (1 * 2) + 1 ```

二、函数式数据处理

1)流的使用

集合是 Java 中最常用的 API。流是 Java API 的新成员,允许以声明式方式处理数据集合,可以看作是遍历数据集的高级迭代器。流可以透明地并行处理,无需额外编写多线程代码。

假设有一组花的集合:

java List<Flower> flowerList = Arrays.asList(new Flower("red", 10), new Flower("yellow", 7), new Flower("pink", 8), new Flower("white", 8), new Flower("black", 12));

需求:获取10块钱以下并且按照价格排序的花的颜色

传统写法java List<Flower> lowPriceFlowers = new ArrayList<>(); for (Flower flower : flowerList) { if (flower.getPrice() < 10) { lowPriceFlowers.add(flower); } } Collections.sort(lowPriceFlowers, new Comparator<Flower>() { @Override public int compare(Flower o1, Flower o2) { return o1.getPrice().compareTo(o2.getPrice()); } }); List<String> lowPriceFlowerColors = new ArrayList<>(); for (Flower priceFlower : lowPriceFlowers) { lowPriceFlowerColors.add(priceFlower.getColor()); }

Java 8 之后,代码可以简化为: java List<String> colorList = flowerList.stream() .filter(t -> t.getPrice() < 10) .sorted(Comparator.comparing(Flower::getPrice)) .map(Flower::getColor) .collect(Collectors.toList());

通过 filter 挑选出10元以下的花,然后通过 sorted 按照花的价格排序,再通过 map 映射出花的颜色,最后通过 collect 将流归约成一个集合

可以使用并行流来加速处理: java flowerList.parallelStream() .filter(t -> t.getPrice() < 10) .sorted(Comparator.comparing(Flower::getPrice)) .map(Flower::getColor) .collect(Collectors.toList());

2)流和集合

集合是一个内存中的数据结构,包含所有当前的值,每个元素都必须先计算出来才能添加到集合中。流则是在概念上固定的数据结构,其元素是按需计算的。换句话说,流就像是一个延迟创建的集合,只有在消费者要求时才会计算值。

只能遍历一次:流只能遍历一次,遍历完后,流已经被消费掉了。你可以从原始数据源那里再获得一个新的流来重新遍历一遍。

3)流的操作

流可以分为三个主要操作:获取流 -> 中间操作 -> 终端操作。

java List<String> colorList = flowerList.stream() .filter(t -> t.getPrice() < 10) .limit(3) .map(Flower::getColor) .collect(Collectors.toList());

中间操作:

终端操作:

4)流的构建

  • 由值创建流:Stream.of() / Stream.empty()
  • 由数组创建流:Arrays.stream()
  • 由文件生成流:File.lines()

5)收集器的使用

假设有一组花的集合:

java List<Flower> flowerList = Arrays.asList(new Flower("red", 10), new Flower("yellow", 7), new Flower("pink", 8), new Flower("yellow", 8), new Flower("red", 12));

按照花的颜色分组:

传统写法java Map<String, List<Flower>> listMap = new HashMap<>(); for (Flower flower : flowerList) { if (null == listMap.get(flower.getColor())) { List<Flower> flowers = new ArrayList<>(); listMap.put(flower.getColor(), flowerList); } listMap.get(flower.getColor()).add(flower); }

Java 8 之后,可以简化为: java Map<String, List<Flower>> map = flowerList.stream() .collect(Collectors.groupingBy(Flower::getColor));

6)分组的使用

假设有一组花的集合:

java List<Flower> flowerList = Arrays.asList(new Flower("red", 10), new Flower("yellow", 7), new Flower("pink", 8), new Flower("yellow", 8), new Flower("red", 12));

按照颜色分组:

java Map<String, List<Flower>> color = flowerList.stream() .collect(Collectors.groupingBy(Flower::getColor));

统计每种颜色的数量:

java Map<String, Long> longMap = flowerList.stream() .collect(Collectors.groupingBy(Flower::getColor, Collectors.counting()));

三、学会使用 Optional

开发中最常见的异常之一就是 NullPointerException。Java 8 引入了 Optional 来解决这个问题。

传统写法java private String getPhoneType(Person person) { if (person != null) { Phone phone = person.getPhone(); if (phone != null) { return phone.getType(); } } return ""; }

每次引用都做一次判空操作,虽然避免了空指针异常,但代码显得臃肿。

使用 Optional

java private String getPhoneType(Person person) { return Optional.ofNullable(person) .map(Person::getPhone) .map(Phone::getType) .orElse(""); }

四、新的日期和时间

在 Java 8 之前,我们主要依赖 java.util.Date 类,但它的表现方式并不直观。Java 8 引入了新的日期和时间 API,如 LocalDateLocalTimeLocalDateTime

LocalDate

创建一个 LocalDate 对象:

java LocalDate nowDate = LocalDate.of(2020, 7, 18); int year = nowDate.getYear(); Month month = nowDate.getMonth(); int day = nowDate.getDayOfMonth(); DayOfWeek dayOfWeek = nowDate.getDayOfWeek(); int days = nowDate.lengthOfMonth(); LocalDate nowdate = LocalDate.now();

也可以使用 TemporalField 读取 LocalDate 的值:

java LocalDate nowDate = LocalDate.of(2020, 7, 18); int year = nowDate.get(ChronoField.YEAR); int month = nowDate.get(ChronoField.MONTH_OF_YEAR); int day = nowDate.get(ChronoField.DAY_OF_MONTH);

LocalTime

创建一个 LocalTime 对象:

java LocalTime nowTime = LocalTime.of(19, 34, 32); int hour = nowTime.getHour(); int minute = nowTime.getMinute(); int second = nowTime.getSecond();

也可以使用 TemporalField 读取 LocalTime 的值:

java LocalTime nowTime = LocalTime.of(19, 34, 32); int hour = nowTime.get(ChronoField.HOUR_OF_DAY); int minute = nowTime.get(ChronoField.MINUTE_OF_HOUR); int second = nowTime.get(ChronoField.SECOND_OF_MINUTE);

LocalDateTime

LocalDateTime 可以合并日期和时间:

java LocalDate nowDate = LocalDate.of(2020, 7, 18); LocalTime nowTime = LocalTime.of(19, 45, 20); LocalDateTime dt1 = LocalDateTime.of(2020, Month.JULY, 18, 19, 45, 20); LocalDateTime dt2 = LocalDateTime.of(nowDate, nowTime); LocalDateTime dt3 = nowDate.atTime(19, 45, 20); LocalDateTime dt4 = nowDate.atTime(nowTime); LocalDateTime dt5 = nowTime.atDate(nowDate); LocalDate date1 = dt1.toLocalDate(); LocalTime time1 = dt1.toLocalTime();

日期-时间类的通用方法:

Duration 和 Period

这两个类用来表示两个日期或时间之间的间隔:

java Duration d1 = Duration.between(time1, time2); Duration d1 = Duration.between(dateTime1, dateTime2); Duration threeMinutes = Duration.ofMinutes(3); Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES); Period tenDays = Period.ofDays(10); Period threeWeeks = Period.ofWeeks(3); Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);

日期-时间类中表示时间间隔的通用方法:

通过这些改进,Java 8 提供了更强大、更简洁的工具来处理日期和时间。

    本文来源:图灵汇
责任编辑: :
声明:本文系图灵汇原创稿件,版权属图灵汇所有,未经授权不得转载,已经协议授权的媒体下载使用时须注明"稿件来源:图灵汇",违者将依法追究责任。
    分享
一先讲解最全有没有Java
    下一篇