【Java】Java8基础及其在项目中的使用场景

Java8基础及其在项目中的使用场景

Plan

Java 基础知识提升:

  1. 熟悉Java8特性
  2. 总结出Java8在小西项目中的使用场景

Do

第一个任务:熟悉Java8特性

Java8提供了新特性来提高开发效率。

  1. Lambda表达式(也称为闭包)
  2. 函数式接口
  3. 方法引用与构造器引用
  4. Stream API
  5. 接口的增强:接口的默认方法与静态方法
  6. 新的日期时间 API:Date Time API
  7. Optionl 类
  8. IO/NIO 的改进

其中,引用最广泛的新特性是Lambda表达式和Stream API。

tip:
过了一天后,导师发现我Java8新特性看的有点慢,给我画了重点。
我们项目中经常使用的是Stream API、Lambda表达式(可以私下敲代码巩固)、Optional的ofNullable()方法,知道DateTime API是如何使用的,其他的了解即可如:函数式接口

因此本此只学上面所说重点,记录顺序如下:

  1. Lambda表达式(也称为闭包)
  2. 函数式接口
  3. Stream API
  4. 新的日期时间 API:Date Time API
  5. Optionl 类

1. Lambda表达式(也称为闭包)
Lambda表达式是一个匿名函数,,很多JVM平台上的语言(Groovy、Scala等)从诞生之日就支持Lambda表达式,但是Java开发者没有选择,而是使用匿名内部类来代替Lambda表达式。

我们来看一个例子:比较两个Integer类型数据的大小
匿名内部类的实现

//内部实体类
Comparator<Integer> com = new Comparator<Integer>() {
   @Overridepublicintcompare(Integer o1, Integer o2){
      return Integer.compare(o1, o2); 
}};
//接下来将上述匿名内部类的实例作为参数,传递到其他方法中
TreeSet<Integer> treeSet = new TreeSet<>(com);

Lambda表达式的实现:

Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

上面整个匿名内部类中,真正有用的就是下面一行代码。

return Integer.compare(o1, o2);

其他的代码本质上都是“冗余”的。但是为了书写上面的一行代码,我们不得不在匿名内部类中书写更多的代码。

而Java8引入Lambda表达式真是为了解决这种"冗余"现象,实现简洁而紧凑的语言结构。

Lambda 表达式形式:
一个最基本的 Lambda 表达式,它由三部分组成:包含一个 Lambda 表达式的运算符 ->,在运算符的左边是输入参数,右边则是函数主体。
在这里插入图片描述参数e的类型是由编译器推理得出的,你也可以显式指定该参数的类型,例如:
在这里插入图片描述
如果Lambda表达式需要更复杂的语句块,则可以使用花括号将该语句块括起来:
在这里插入图片描述
Lambda表达式可以引用类成员和局部变量(会将这些变量隐式得转换成final的,且不可以改变值),例如下列两个代码块的效果完全相同:
在这里插入图片描述

在这里插入图片描述
不可以修改局部变量:
在这里插入图片描述
Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量:
在这里插入图片描述

2. 函数式接口
如果一个接口中,**只声明了一个抽象方法,**则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口。
而Lambda表达式的本质: 作为函数式接口的实例,也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。所以以前用匿名实现类表示的现在都可以用Lambda表达式来写
函数式接口有下面几个特点:

  1. 接口有且仅有一个抽象方法;
  2. 允许定义静态方法;
  3. 允许定义默认方法;
  4. 允许 java.lang.Object 中的 public 方法;
  5. 推荐使用 @FunctionInterface 注解(如果一个接口符合函数式接口的定义,加不加该注解都没有影响,但加上该注解可以更好地让编译器进行检查)。

例子如下:

@FunctionalInterface
interface TestFunctionalInterface
{
    //抽象方法
    public void doTest();
    //java.lang.Object中的public方法
    public boolean equals(Object obj);
    public String toString();
    //默认方法
    public default void doDefaultMethod(){System.out.println("call dodefaultMethod");}
    //静态方法
    public static void doStaticMethod(){System.out.println("call doStaticMethod");}


    public static void main(String...s){
        //实现抽象方法
        TestFunctionalInterface test = ()->{
            System.out.println("call doTest");
        };
        //调用抽象方法
        test.doTest();
        //调用默认方法
        test.doDefaultMethod();
        //调用静态方法
        TestFunctionalInterface.doStaticMethod();
        //调用toString方法
        System.out.println(test.toString());
    }
}

JDK 8 之后新增了一个函数接口包 java.util.function 这里面包含了我们常用的一些函数接口:
在这里插入图片描述
例子:

    @Test
    public void test2(){
        List<String> list = Arrays.asList("北京","南京","天津","东京","西京","普京");

        List<String> filterStrs = filterString(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("京");
            }
        });

        System.out.println(filterStrs);


        List<String> filterStrs1 = filterString(list,s -> s.contains("京"));
        System.out.println(filterStrs1);
    }

    //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
    public List<String> filterString(List<String> list, Predicate<String> pre){

        ArrayList<String> filterList = new ArrayList<>();

        for(String s : list){
            if(pre.test(s)){
                filterList.add(s);
            }
        }
        return filterList;
    }

3. Stream API
Java 8 引入了流式操作(Stream),通过该操作可以实现对集合(Collection)的并行处理和函数式操作。

学习流式操作,就是学习java.util.stream包下的API,我们称之为Stream API,它把真正的函数式编程引入到了 Java 中。

根据流的并发性,流又可以分为串行和并行两种,而流式操作可实现集合的过滤、排序、映射等功能。Stream通过串行、并行的方式处理大批量数据,提高处理效率。

根据操作返回的结果不同,流式操作分为中间操作和最终操作两种。
中间操作只对操作进行了记录,即只会返回一个流,不会进行计算操作,这样就可以将多个操作依次串联起来;而终结操作是实现了计算操作,返回一特定类型的结果。

我们开发一般是用Stream API通过Lambda表达式对集合进行各种非常便利高效的大批量数据操作
案例如下:

// java.util.Collection
default Stream<E> stream() {
 return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
 return StreamSupport.stream(spliterator(), true);
}
 
@Data
class Student {
 private Integer height;
 private String sex;
}
Map<String, List<Student>> map = Maps.newHashMap();
List<Student> list = Lists.newArrayList();
// 传统的迭代方式
for (Student student : list) {
 if (student.getHeight() > 160) {
 String sex = student.getSex();
 if (!map.containsKey(sex)) {
 map.put(sex, Lists.newArrayList());
 }
 map.get(sex).add(student);
 }
}
// Stream API,串行实现
map = list.stream().filter((Student s) -> s.getHeight() > 160).collect(Collectors.groupingBy(Student::getSex));
// Stream API,并行实现
map = list.parallelStream().filter((Student s) -> s.getHeight() > 160).collect(Collectors.groupingBy(Student::getSex));

Tips:
Stream 和 Collection 集合的区别:

Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。

注意点:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

Stream 的操作三个步骤

1- 创建 Stream
一个数据源(如:集合、数组),获取一个流
2- 中间操作
一个中间操作链,对数据源的数据进行处理
3- 终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
创建 Stream方式,常用中间操作,常用终止操作,请参考文章:Java 流式操作

4. Date Time API

Java8之前的 Date Time API有如下缺点:

  • 可变性:旧的 API 日期和时间是可变的,日期和时间这样的类应该是不可变的;
  • 偏移性:Date 中的年份是从 1990 年开始,月份是从 0 开始,星期天是用 1 表示的,不了解 API 的开发者很容易用错;
  • 格式化:SimpleDateFormat 只能用于格式化 Date 类型,不能格式化 Calendar 类型。

Java8 Date Time API针对于以上问题改进如下:

  • 在java.time 包中,提供了更优秀易用的 API。包含了 LocalTime(本地时间)、LocalDate(本地日期)、LocalDateTime(本地日期时间)、ZonedDateTime(带时区的日期时间)和 Duration(时间间隔)类。
  • 在java.util.Date 类下面增加了 toInstant() 方法,用于把 Date 转换为新的类型
  • 月份使用 1~12 表示 1 月 到 12 月,星期使用 1 ~ 7 表示星期一到星期天。
  • 使用了新的 DateTimeFormatter 来取代旧的 SimpleDateFormat。

案例如下:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

/**
 * @author colorful@TaleLin
 */
public class LocalDateTimeDemo1 {

    public static void main(String[] args) {
        // 获取当前日期
        LocalDate localDate = LocalDate.now();
        // 获取当前时间
        LocalTime localTime = LocalTime.now();
        // 获取当前日期和时间
        LocalDateTime localDateTime = LocalDateTime.now();
        // 打印
        System.out.println(localDate);
        System.out.println(localTime);
        System.out.println(localDateTime);
    }
}

运行结果:

2020-06-10
14:17:48.618294
2020-06-10T14:17:48.618421

在实际开发中,LocalDateTime 相较于 LocalDate 和 LocalTime 更加常用
本地日期和时间通过 now() 获取到的总是以当前默认时区返回的。

另外,可以使用 of() 方法来设置当前日期和时间:

// 2020-9-30
LocalDate date = LocalDate.of(2020, 9, 30);
// 14:15:10
LocalTime time = LocalTime.of(14, 15, 10);
// 将date和time组合成一个LocalDateTime
LocalDateTime dateTime1 = LocalDateTime.of(date, time);
// 设置 年、月、日、时、分、秒
LocalDateTime dateTime2 = LocalDateTime.of(2020, 10, 21, 14, 14);

5. Optionl 类

Java应用中最常见的bug就是空值异常,使用Optionl 可以避免这个问题。
Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在,或者仅仅保存null,表示这个值不存在。它提供了一些有用的接口来避免显式的null检查。

Optional常用的使用场景:空判断。
空判断主要是用于不知道当前对象是否为NULL的时候,需要设置对象的属性。不使用Optional时候的代码如下:

if(null != order){
    order.setAmount(orderInfoVo.getAmount());
}

使用Optional时候的代码如下:

Optional.ofNullable(order).ifPresent(o -> o.setAmount(orderInfoVo.getAmount()));

参考文章:Java函数式编程之Optional

但注意一个事情,Optional.ofNullable()只能判断是否为null这一种情况,不能用于判断字符串是否为空字符串(对于字符串的判空,极不建议使用optional 来实现,因为字符串的空包含了多种情况:null 、“null” 、“”、“ ” 所以还是建议老老实实用StrUtil.isBlank方法对于对象的判空,还是用处挺大的)

第二个任务:总结出Java8在小西项目中的使用场景
1.Lambda表达式
2.Stream API
小西中,Lambda表达式和Stream API大多数情况下会结合使用。

如清理空库存方法:
从数据库中获取到商品列表后,把它转成流对象并进行遍历,并把流对象里面的每个数据的属性MallConstants变为ENABLE_FALSE,再更新数据库.

    @Override
    public void cleanEmptyStockTask() {
        List<GoodsSku> goodsSkuList = baseMapper.selectList(Wrappers.<GoodsSku>lambdaQuery().eq(GoodsSku::getStock, 0));
        goodsSkuList.stream().forEach(goodsSku -> {
            if (goodsSku.getEnable().equals(MallConstants.ENABLE_TRUE)) {
                goodsSku.setEnable(MallConstants.ENABLE_FALSE);
                baseMapper.updateById(goodsSku);
            }
        });

3.DateTime API
小西中主要用在签到,更新会场日期,日期工具类。
LocalDateTime 相较于 LocalDate 和 LocalTime用的会多一些。

4.Optional的ofNullable()方法
主要用在判断对象是否为空

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值