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() {} } ```
开发过程中,我们需要适应不断变化的需求,一种有效的方式是行为参数化。
需求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()));
这种方式可以使代码更加简洁和灵活。
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
@FunctionalInterface public interface Runnable { void run(); }
@FunctionalInterface public interface ActionListener extends EventListener { void actionPerformed(ActionEvent e); }
@FunctionalInterface
public interface Callable
Lambda 表达式可以直接为函数式接口的抽象方法提供实现,并将整个表达式作为函数式接口的实例。
java
Runnable runnable = () -> System.out.println("这是运用 Lambda 的写法");
Predicate
这个接口定义了一个 test() 的抽象方法,它接受泛型 T 对象并返回一个 boolean 类型的值。当你需要表示一个涉及类型 T 的布尔表达式时,可以使用这个接口。
```java
public static List
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
(Flower f) -> f.getPrice(); // ==> Flower::getPrice
flowerList.stream().map(t -> t.getPrice()).collect(Collectors.toList()); // ==> flowerList.stream().map(Flower::getPrice).collect(Collectors.toList()); ```
我们有一组鲜花集合如下:
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
Predicate
Predicate
用于 Function 接口:
andThencompose```java
Function
Function
集合是 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());
集合是一个内存中的数据结构,包含所有当前的值,每个元素都必须先计算出来才能添加到集合中。流则是在概念上固定的数据结构,其元素是按需计算的。换句话说,流就像是一个延迟创建的集合,只有在消费者要求时才会计算值。
只能遍历一次:流只能遍历一次,遍历完后,流已经被消费掉了。你可以从原始数据源那里再获得一个新的流来重新遍历一遍。
流可以分为三个主要操作:获取流 -> 中间操作 -> 终端操作。
java
List<String> colorList = flowerList.stream()
.filter(t -> t.getPrice() < 10)
.limit(3)
.map(Flower::getColor)
.collect(Collectors.toList());
Stream.of() / Stream.empty()Arrays.stream()File.lines()假设有一组花的集合:
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));
假设有一组花的集合:
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()));
开发中最常见的异常之一就是 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,如 LocalDate、LocalTime 和 LocalDateTime。
创建一个 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 对象:
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 可以合并日期和时间:
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();
日期-时间类的通用方法:
这两个类用来表示两个日期或时间之间的间隔:
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 提供了更强大、更简洁的工具来处理日期和时间。