浅尝 java8 新特性

本文介绍了Java8的重要特性,包括Optional的使用,避免空指针异常,提供了更安全的代码实践。接着讲解了Lambda表达式,包括无参和带参函数式接口的示例,简化了代码。最后,详细阐述了流式编程的概念,通过实例展示了如何利用filter(), map(), sorted()等中间操作和forEach(), collect()等结束操作进行数据处理。
摘要由CSDN通过智能技术生成

Java8 新特性

Java 8 是目前使用的最广的 jdk 版本,其中包含了很多新特性,这里,我们就来好好学习一下 java8 的新特性

这篇文章可能不会将 Java8 新特性介绍得那么全,因为这些知识点,都是我在企业型目中碰到的,都是一些比较常见的内容,可能会有部分不怎么常用的部分,我这里介绍不到

一、集合

1、Optional

1)Optional 介绍

Optional类实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional 类的引入很好的解决空指针异常。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。尽量避免在程序中直接调用Optional对象的get()和isPresent()方法,避免使用Optional类型声明实体类的属性。

Optional 和 对象的关系,就像是父母和小孩的关系:

如果小孩(对象)没有父母的管教,那这个小孩可能会光着屁股满街跑(空指针),引发不必要的麻烦

而有了父母(Optional)的管教,当小孩(对象)还想光屁股(空指针)的时候,就会被父母抓住,强行穿上裤子(伪造非空对象)

2)Optional 演示

我们来演示一下,Optional 遍历一个空的列表的时候,该怎么操作:

// 空的列表
ArrayList<Integer> list = null;
// Optional中包了一个空的列表,如果为空,我们就新建一个 ArrayList对象(orElse(new ArrayList<>())),然后遍历它,从而避免空指针
Optional.ofNullable(list).orElse(new ArrayList<>()).stream().forEach(System.out::println);

遍历空

我们也可以抛出自定义异常:

ArrayList<Integer> list = null;
Optional.ofNullable(list).orElseThrow(()->new Exception("对象不能为空"));

抛出异常

3)Optional 方法介绍

方法介绍

3)Mybatis 使用 Optional

Mybatis 3.5 之后,支持返回 Optional 类型了

这样,我们就可以愉快的进行非空判断了

传统写法:

@Mapper
public interface UserMapper {
    @Select("select * from user where id = #{id}")
    User selectById(Long id);
}
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @GetMapping("/{id}")
    public User findById(@PathVariable Long id) {
        User user = this.userMapper.selectById(id);
        if(user == null) {
          // 抛异常,或者做点其他事情(麻烦)
        }
    }
}

Optional 写法:

@Mapper
public interface UserMapper {
    @Select("select * from user where id = #{id}")
    Optional<User> selectById(Long id);
}
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @GetMapping("/{id}")
    public User findById(@PathVariable Long id) {
        return this.userMapper.selectById(id)
                .orElseThrow(() -> new IllegalArgumentException("This user does not exit!"));
    }
}

二、Lambda 表达式

Java 8 的一大更新,就是 Lambda 表达式,只是 java 中的 lambda 表达式是阉割版的

java 中的 lambda 表达式,只能用在 函数式接口 上,即这个接口只能有一个方法

下面,我们就来具体看一下 lambda 表达式的使用

1、无参函数式接口

无参函数式接口,我们最熟悉的,就是 Runnable 接口了

// 这个注解,表示是一个函数式接口,可有可无
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

我们平时使用的时候,是这样的:

new Thread(new Runnable() {
  @Override
  public void run() {
    for (int i = 0; i < 10; i++) {
      System.out.println("哈哈哈");
    }
  }
}).start();

使用匿名内部类,我们就不用再显示的写一个类,继承 Runnabe 接口,重写 run() 方法

但是,使用 lambda 表达式,我们可以更加简洁的编写上面的代码:

new Thread( () -> {
  for (int i = 0; i < 10; i++) {
    System.out.println("哈哈哈");
  }
}).start();

省去了被重写的方法名和 @Override 注解

如果是单行的实现,我们甚至可以省略大括号:

// 我们自定义函数式接口,和调用这个函数式接口的函数
@FunctionalInterface
interface MyInterface {
  int getNum();
}

public static void judgeNum(MyInterface myInterface) {
  if (myInterface.getNum()==10) {
    System.out.println("不错,获得了我想要的数");
  } else {
    System.out.println("不是我想要的数");
  }
}
// 使用这个函数
judgeNum(() -> 10); // 不错,获得了我想要的数

这里,除了单行实现不需要大括号以外,我们还要注意一点,就是单行的实现,不能显示的写 return,因为如果有返回值的话,是默认 return 的

2、带参函数式接口

如果我们的函数式接口带参,是要在括号中加上参数就好

区别在于,括号中加上的参数,不需要加上参数类型,因为程序会自动进行推断

带参函数式接口的最好的例子,就是 Comparator

不使用 lambda 表达式的话,我们是这么实现的:

List<Integer> list = Arrays.asList(3, 5, 4, 2, 4, 6, 7);
Collections.sort(list, new Comparator<Integer>() {
  @Override
  public int compare(Integer o1, Integer o2) {
    return o1-o2;
  }
});

使用 lambda ,是这么实现的:

List<Integer> list = Arrays.asList(3, 5, 4, 2, 4, 6, 7);
Collections.sort(list,(o1,o2) -> o1-o2);

可以发现,代码量是大大减少了

我们讲 lambda,不仅是为了偷懒,更重要的是,接下来要介绍的 java8 流式编程,是需要依靠 lambda 表达式的。

二、流式编程

流式编程是 Java8 中比较重要的一个部分

使用流式编程,之前很多 for 循环的遍历,都可以大大缩减

**把大象关进冰箱有三步:**打开冰箱,把大象放进冰箱,关上冰箱

**流式编程也是三步:**开启流,中间操作,结束操作

这么说,可能有人要骂街了,那我们就用一个实际的例子来讲一讲:

需求:筛选出 List 中元素为 1 的数,并进行收集

List<Integer> list = Arrays.asList(3, 2, 2, 4, 21, 1, 341, 1);

1、开启流

list.stream.<这里是中间操作>.<这里是结束操作>;

2、中间操作

list.stream().filter(elem -> elem==1).<这里是结束操作>;

3、结束操作

list.stream().filter(elem -> elem==1).collect(Collectors.toList());

筛选后的结果

下表,汇总了 stream 接口的常见方法:

stream接口常见方法

1、获得流

要想进行流式编程,我们起码要先获得流

获得流有两个方式

  • 使用 stream().of() 方法
  • 调用集合的 stream() 方法
// 方式1:
Stream<Integer> stream = Stream.of(3, 2, 2, 4, 21, 341);

// 方式2:
List<Integer> list = Arrays.asList(3, 2, 2, 4, 21, 341);
list.stream();

2、中间操作

中间操作

中间操作有不少,这里,我介绍几个比较常见的中间操作

1)distinct()

去重

List<Integer> list = List.of(2, 4, 2, 1, 4, 5, 8, 1, 1, 1, 1);
list = list.stream().distinct().collect(Collectors.toList());
System.out.println(list);

去重结果

2)filter()

过滤

按照我们需要的内容,对进行过滤

List<Integer> list = List.of(2, 4, 2, 1, 4, 5, 8, 1, 1, 1, 1);
// 只要流中为 1 的元素
list = list.stream().filter(obj -> obj==1).collect(Collectors.toList());
System.out.println(list);

image-20210809165500671

3)map()

将数据转换为我们希望的形式

// 将字符串全部转换为大写
List<String> list = List.of("abc", "def", "ghi");
list = list.stream().map(obj -> obj.toUpperCase()).collect(Collectors.toList());
System.out.println(list);

转换为大写

4)sorted()

元素排序

需要我们自己传入一个比较器

List<Integer> list = List.of(2, 4, 2, 1, 4, 5, 8, 1, 1, 1, 1);
list = list.stream().sorted((o1,o2) -> o1-o2).collect(Collectors.toList());
System.out.println(list);

剩下的不是太常用,但是使用方法和上面的是大同小异的,如果有需要的话,可以去翻阅相关文档。

3、结束操作

结束操作

结束操作,我们主要介绍一个 forEach()collect()

1)forEach()

forEach()会遍历流中的所有元素,

我们一般用这个方法,快读打印元素信息

List<Integer> list = List.of(2, 4, 2, 1, 4, 5, 8, 1, 1, 1, 1);
// System.out::println 是调用 System.out 类中的 println 方法
// 通过这个方法,可以调用该类中的静态方法
// 格式是 类名::方法名
list.stream().forEach(System.out::println);

打印元素信息

2)collect()

collect 的操作,可以变得十分复杂,可以通过下游收集器,实现类似于 SQL 的操作,但是,在一般业务中,我们不太可能会有如此离谱的需求,因为这些事都交给 SQL 去做了

这里,我们熟悉一下怎么通过收集器,将流变为 集合 即可

我们最常用的操作,是将流变为 List 和 Set:

List<Integer> list = List.of(2, 4, 2, 1, 4, 5, 8, 1, 1, 1, 1);
List<Integer> myList = list.stream().collect(Collectors.toList());
Set<Integer> mySet = list.stream().collect(Collectors.toSet());

这两个方法,返回的是一个接口,我们并不知道底层实现

如果想指定底层实现,我们需要这么写:

List<Integer> list = List.of(2, 4, 2, 1, 4, 5, 8, 1, 1, 1, 1);
// 我们指定底层实现是 ArrayList 和 HashMap
List<Integer> myList = list.stream().collect(Collectors.toCollection(ArrayList::new));
Set<Integer> mySet = list.stream().collect(Collectors.toCollection(HashSet::new));
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FARO_Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值