LHJ's Blog

JAVA8新特性

⛅ 接口默认方法、静态方法

在Java8之前,接口中只能是抽象的方法,不允许有方法的实现,这样带来一个问题就是:如果已经定义好了一个接口MyInterface,然后下面有若干个实现类,那么如果哪一天需要为这个接口添加新特性,比如添加一个新的方法newMethod,那么下面的若干实现类就需要去实现这个方法,非常麻烦。简而言之就是不兼容…

Java8引入了默认方法,在接口中使用default关键字声明,实现类实现这个接口的时候可以不实现这个方法而直接使用

1
2
3
4
5
6
7
8
9
public interface DefaultInterface {

public abstract void oldShow();

default void newShow(){
System.out.println("Hello World");
}

}

Java8的接口还支持静态方法,在接口中使用static关键字声明,至于静态方法和默认方法的区别(静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface DefaultInterface {

public abstract void oldShow();

default void newShow(){
System.out.println("Hello World");
}

static void staticShow(){
System.out.println("Hello World");
}

}

⛅ 函数式接口

函数式接口主要是为了引入函数式编程这么一个概念。Java是一个面向对象的语言,一切皆对象,而函数式编程可以把一个函数作为参数进行传递,函数是可以独立存在的。这个和Java的面向对象的原则违背。然而函数式编程越来越流行,Java也提供了语法上的新特性,支持函数式编程。

函数式接口的定义是:一个接口中只能有一个抽象的方法(默认方法、静态方法不在此列)。默认的如果接口只有一个抽象方法,那么会直接转化为函数式接口,你也可以通过添加@FunctionalInterface来显示声明这是一个函数式接口,这样的好处就是可以防止后期不小心多添加了抽象方法,导致函数式接口失效

1
2
3
4
5
6
7
8
package com.longhujing.mybatis.plus.guide.quickstart.service;

@java.lang.FunctionalInterface
public interface FunctionalInterface {

void show();

}

⛅ Lambda表达式

传统的使用匿名内部类创建一个线程的方法是这样的

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {

public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}).start();
}

}

一共12行代码,但是真正有意义的只是第7行的System.out.println("Hello World");,其余的都是为了满足语法要求不得不写的多余代码,写起来很臃肿。

引入Lambda表达式以后是这样的

1
2
3
4
5
6
7
public class Main {

public static void main(String[] args) {
new Thread(() -> System.out.println("Hello World")).start();
}

}

一行搞定!看着也更舒服了。

✨ Lambda表达式的一些语法规则

最全的Lambda表达式的写法是这样的

1
2
3
4
5
(param1, param2, param3, ... ) -> {
//code1
//code2
return xxx;
}
  • 如果方法体只是一行,可以去掉大括号:(params...) -> your code in line;
  • 如果方法体有多行就必须要有大括号了
  • Lambda表达式不需要为参数以及返回值指定类型,都是由编译器自动进行推导
  • 参数和方法体之间需要通过->连接

⛅ Optional

Optional的引入主要是为了解决NullPointException的问题。传统的为了避免抛出NullPointException异常,需要使用if-else进行判断

1
2
3
4
5
public String getUsername(User user){
if(user != null){
return user.getUsername();
}
}

这样看起来很不爽,而且还挺麻烦的。使用Optional虽然感觉还是挺麻烦,但是至少美观了很多

1
2
3
4
public String getUsername(User user){
Optional<User> optional = Optional.ofNullable(user);
return optional.map(User::getName).orElse(null);
}

Optional可以看作是一个容器,把对象装进去,然后由容器来判断对象是不是空的。即将需要程序员手动执行的操作交给容器完成。

1
2
3
4
5
6
7
//创建Optional的方式
//创建一个空的Optional
Optional optional1 = Optional.empty();
//创建一个Optional,放置一个对象进去,如果对象为空,立刻抛出NullPointException
Optional optional1 = Optional.of(object);
//创建一个Optional,放置一个对象进去,允许对象为空
Optional optional2 = Optional.ofNullable(object);

Optional还可以通过isPresent方法来判断内部的对象是不是null,如果是null,会返回false,否则是true

1
2
3
4
5
public String getUsername(User user){
Optional<User> optional = Optional.ofNullable(user);
System.out.println(optional.isPresent()); //not null -> true else false
return optional.map(User::getName).orElse(null);
}

Optional还可以在容器中的对象是空的情况下,返回一个指定的值

1
2
3
4
//如果optional里面的对象为null,那么返回otherObject
optional.orElse(otherObject);
//如果optional里面的对象为null,那么返回otherOptional.get() -- 即otherOptional里面的对象
optional.getOrElse(otherOptional);

⛅ Stream API

Stream API是Java8的一大亮点,它并不是像InputStream或OutputStream那样的流,而是对Collection操作的一种增强,借助Lambda表达式的特性,可以更高效的对Collection进行一系列操作。

✨ 一个例子说明Stream API的强大

如果有这么一个场景:有一个订单列表,筛选出已经付过款的订单,输出按照订单金额降序排序。

在Java8之前会这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {
//给定的订单列表
List<Order> orders = new ArrayList<>();
List<Order> payedOrders = new ArrayList<>();
for (Order order : orders) {
if (order.isPayed()) {
payedOrders.add(order);
}
}
payedOrders.sort(new Comparator<Order>() {
@Override
public int compare(Order o1, Order o2) {
return o1.getPrice() - o2.getPrice();
}
});
for (Order order : orders) {
System.out.println(order);
}
}

Java8的时候可以这么做:

1
2
3
4
5
public static void main(String[] args) {
//给定的订单列表
List<Order> orders = new ArrayList<>();
orders.stream().filter(Order::isPayed).sorted(Comparator.comparing(Order::getPrice).reversed()).forEach(System.out::println);
}

一行搞定,简直不要太舒服,而且代码也比前面的整洁的多。

✨ Stream API 使用

🔍 创建一个Stream

Collection提供了两种方式:Collection.stream()或Collection.parallelStream(),这两种方式的主要区别是前者是一个顺序流,后者是一个并行流。

1
2
3
//以List为例
list.stream(); //创建一个顺序流
list.parallelStream(); //创建一个并行流

如果是一个数组,可以通过Arrays中的stream()来获取流。

1
2
int[] arr = new int[10];
Arrays.sream(arr);

还可以通过Stream中的静态方法of()获取流

1
Stream.of(1, 2, 3, 4, 5);

或是通过Stream中的静态方法generate()方法生成流

1
2
//创建一个包含两个随机数的流
Stream.generate(Math::random).limit(2);
🔍 Stream API的常用操作
  • filter,过滤掉不符合预期的数据

    1
    2
    //过滤掉大于2的数字
    Stream.of(1, 2, 3, 4, 5).filter(num -> num > 2);
  • skip(n),跳过前面n个元素

    1
    Stream.of(1, 2, 3, 4, 5).skip(2);
  • limit(n),保留前面n个元素

    1
    Stream.of(1, 2, 3, 4, 5).limit(2);
  • distinct(),通过对象的hashcode和equals方法进行去重操作

    1
    Stream.of(1, 2, 2, 2, 3, 4).distinct();
  • map,关系映射,将元素转化为其他信息

    1
    2
    3
    4
    5
    Stream.of("aa", "bb", "cc").map(str -> str.toUpperCase()).forEach(System.out::println());
    //输出:
    //AA
    //BB
    //CC
  • sorted,排序,默认是从小到大自然排序

    1
    2
    3
    4
    5
    Stream.of(5, 4, 3, 2, 1).sorted().forEach(System.out::print);
    //输出:
    //12345
    //也可以自定义排序方法
    Stream.of(1, 2, 3, 4, 5).sorted(((o1, o2) -> o1 - o2));
  • allMatch,检查元素是否都满足某一条件

    1
    Stream.of(1, 2, 3, 4 ,5).allMatch(e -> e > 0);
  • anyMatch,只要元素中有一个满足条件就返回true

    1
    Stream.of(1, 2, 3, 4, 5).anyMatch(e -> e >= 5)
  • noneMatch,检查是否没有一个元素满足条件

    1
    Steram.of(1, 2, 3, 4, 5).noneMatch(e -> e < 0);
  • count,返回流中的数据总数

  • max,返回流中的最大值

  • min,返回流中的最小值

  • collect,将元素整理成一个(list、set、map或一个字符串)

    1
    2
    3
    4
    5
    6
    //整理成一个list
    Stream.of(1, 2, 3).collect(Collectors.toList());
    //整理成一个set
    Stream.of(1, 2, 3).collect(Collectors.toSet());
    //整理成一个map,以name为键,以age为key
    persons.stream.collect(Collectors.toMap(Person::getName, Person::getAge));

⚠ 注意事项

  • Stream API并不会存储数据,只是对数据的一次遍历
  • Stream API是单向的,就像水一样,一去不复返
  • Stream操作是延迟执行的,只有需要的时候才会执行❗

⛅ Date Time API

Java8之前的日期时间API存在很多问题:

  • 线程安全问题:Java8之前的时间日期API不是线程安全的
  • 不支持国际化,时区处理麻烦

Java8新发布的Date Time API进一步强化了时间日期的操作,主要用到的类有

  • Instant,表示时间轴上的一个时间点,Instant.now()返回当前的瞬时时间(格林威治时间)
  • Duration,用于表示两个瞬时时间点的时间差
  • LocalDate,包含年份、月份、天数的日期
  • LocalDateTime,比LocalDate多了小时、分钟、秒数
  • DateTimeFormatter,日期格式化类


 评论