美文网首页
java8新特性

java8新特性

作者: 轻轻敲醒沉睡的心灵 | 来源:发表于2022-03-31 15:18 被阅读0次

介绍Java8之前,先回想一下基础知识:Java中的接口和抽象类 - 简书 (jianshu.com)

1. 函数式接口

先举一个例子,我们经常使用到的:Java中在用到线程的时候,经常是new一个类实现Runnable接口重写run(),然后将这个类的实例放到Thread()中。我们看一下Runnable接口

1648538659(1).png
我们使用这个接口时,也可以用匿名内部类(叫这个名字,我感觉是因为,平时我们用接口都会new一个类去实现接口,然后用这个类的实例,这里没有new类,所以没有名字,但是像类一样,重写了方法,也有实例了)的方法:
new Runnable() {
            
    @Override
    public void run() {
        System.out.println("新线程处理业务");
    }
};

这是以前经常使用的代码样例。为了简化代码,Java8引入了Lambda表达式,而Lambda表达式的基础就是函数式接口。
函数式接口有以下特点:

  • 1.在接口中只能够允许有一个抽象方法,但是可以有多个非抽象方法
  • 2.在函数接口中可以重写Object父类中的方法,如:toString()hashCode()
  • 3.可以加@FunctionalInterface注解表示该接口为函数接口

1.1 Java8内置的函数式接口

Java8内置了很多函数式接口,在java.util.function包中,平时用的最多的主要是以下几个,其他的可以ctrl+shift+t自己看

函数式接口 入参 返回 用法
Conusmer<T> T 消费型接口,内含方法:
void accept(T t)
接收入参,无返回值
Supplier<T> T 供给型接口,内含方法:
T get()
无入参,有返回值
Function<T ,R> T R 函数型接口,内含方法:
R apply(T t)
有入参,有返回值
Predicate<T> T boolean 断言型接口,内含方法:
boolean test(T t)
接收入参,返回boolean值

2. Lambda表达式

2.1 语法结构

  • () 参数列表,是指接口中,抽象方法要传的参数
  • -> 分隔,我感觉这个就代表执行添加方法体的方法
  • {} 方法体,是指我们要重写的抽象方法的方法体
    (a,b)->{ }

2.2 例子

都已Java8内置的函数式接口为例。

  • 无参无返回接口
Runnable r = () -> {
    System.out.println("无参无返回接口");
};
r.run();
// 直接调用
((Runnable)() -> {
    System.out.println("无参无返回接口");
}).run();
  • 有参无返回
Consumer<Integer> c = (n) -> {
    System.out.println("有参无返回");
};
c.accept(9);
((Consumer<Integer>)(n) -> {
    System.out.println("有参无返回");
}).accept(3);
  • 无参有返回接口
Supplier<String> s = () -> {
    return "无参有返回";
};
s.get();
((Supplier<String>)() -> {
    return "无参有返回";
}).get();

规则1:若lambda表达式有且只有1个参数,可以省略()
规则2:若lambda表达式方法体只有一条语句,可以省略{},也可以省略return关键字
所以,上面几个简写:

((Runnable)() -> System.out.println("无参无返回接口")).run();
        
((Consumer<Integer>)n -> System.out.println("有参无返回")).accept(3);
        
((Supplier<String>)() -> "无参有返回").get();

2.4 实用

Lambda表达式在代码中很实用的,我在处理集合的时候,有些API中经常用到,典型的:

List<User> list = userDao.selectList(null);
// 排序
list.sort((o1, o2) -> o2.getId() - o1.getId()); 
// 轮循
list.forEach(o1 -> System.out.println(o1.toString()));

2.5 方法引用

什么是方法引用呢?当我们需要一个函数式接口的实例对象,于是我们使用Lambda表达式来创建,lambda的方法体需要我们自己实现,这个时候如果有另外的方法,正好满足我们的需要,我们就不用写了,直接把那个方法拿过来用就行了,但是我们需要的是lambda表达式创建的接口的实例对象,此时 lambda表达式可以引用已有的方法,称为方法引用
比如,我想要Consumer<String>的实例对象,方法体就打印传入的参数,用lambda表达式效果如下面1所示。但是打印传参这个功能呢,在PrintStream这个类中已经有了,我可以直接拿过来用,作为我lambda表达式要实现的方法。

java自带的打印的方法
这个时候就用到了方法引用,用::来表示,如下2所示。
// 1. lambda表达式
Consumer<String> con = text -> System.out.println(text);
con.accept("测试方法引用");
// 2. 方法引用      
Consumer<String> con1 = System.out::println;
con1.accept("test success");

方法引用使用条件:被引用方法的参数列表返回类型必须要和函数接口中方法的参数列表返回类型保持一致。
几种常见的方法引用:

  • 1.静态方法引入
  • 2.实例方法引入
  • 3.对象方法引入
  • 4.构造方法引入

前2种方法比较正常,如下面代码中的1和2,我的理解是:双冒号前面的 能点出来 冒号后面的方法;
对象方法:是指函数式接口中方法的第1个参数 类型是 要引入的方法所在的类的类型,比较特殊,所以有简便写法,也可以按前2种来写,简便写法认识就行;
构造方法引入,下面代码中的3,class :: new

// 1
Consumer<String> con1 = System.out::println;
con1.accept("test success");
// 2    
Consumer<String> con2 = new User()::setName;
con2.accept("张三");
// 3. 构造方法引用        
Supplier<User> su = User::new;

3. Stream Api

Stream 是JDK1.8 中处理集合的关键抽象概念,Lambda 和 Stream 是JDK1.8新增的函数式编程最有亮点的特性了,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL执行的数据库查询。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
特点:

  • stream自己不会存储元素
  • Stream不会改变源对象,它是返回一个持有结果的新Stream
  • Stream操作是延迟执行的,因为要等拿到了需要的结果的时候才执行
    Stream操作分成3步:
  1. 创建Stream流,(用集合或者数组等作为原数据)
  2. 中间操作(过滤、相加、分组等)
  3. 终止操作

3.1 创建Stream流

Stream流分为2种:

  • Stream流,采用单线程执行
  • parallelStream流,为并行流采用多线程执行,效率更高
    几种方式:
public static void main(String[] args) {
    // 1. 通过集合的stream()方法
    List<String> list = new ArrayList<String>();
    Stream<String> s1 = list.stream();
    // 2. 通过Arrays的静态方法stream()
    User[] userArr = new User[10];  
    Stream<User> s2 = Arrays.stream(userArr);
    // 3. 通过Stream的静态方法of()
    Stream<String> s3 = Stream.of("a", "b", "c");
    // 4. 创建无限流
    Stream<Integer> s4 = Stream.iterate(1, x -> x+2);
    s4.limit(10).forEach(System.out::println);
    Stream<Double> s5 = Stream.generate(() -> Math.random());
    s5.limit(10).forEach(System.out::println);
}

3.2 Stream流操作

    1. 筛选 filter
List<User> list = userDao.selectList(null);
list.stream()
    .filter(u -> u.getAge() == 22)
    .forEach(System.out::println);
// foreach是终止操作,有终止操作,才有结果
    1. 截断 limit
List<User> list = userDao.selectList(null);
list.stream()
    .limit(3)
    .forEach(System.out::println);
    1. 跳过 skip
List<User> list = userDao.selectList(null);
list.stream()
    .skip(2)
    .forEach(System.out::println);
    1. 去重 distinct
      需要注意,去重比较的是Hashcode和equals方法
List<User> list = userDao.selectList(null);
list.stream()
    .distinct()
    .forEach(System.out::println);
  • 5 映射 map 理解为 从集合元素(对象)中拿到想要的属性,并将拿到的数据放到了新流中
List<User> list = userDao.selectList(null);
list.stream()
    .map(User::getName)   // 拿到每个元素的名字
    .forEach(System.out::println);
  • 6 排序 sorted
List<User> list = userDao.selectList(null);
list.stream()
    .sorted((u1, u2) -> u1.getName().compareTo(u2.getName()))
    .forEach(System.out::println);
  • 7 终止操作 查找和匹配
    • allMatch -- 是否匹配所有元素
    • anyMatch -- 是否至少匹配一个元素
    • noneMatch -- 是否没有匹配的元素
    • findFirst -- 找到第一个元素
    • findAny -- 任意一个
    • count -- 流中元素个数
    • max -- 流中最大值
    • min -- 流中最小值
List<User> list = userDao.selectList(null);
boolean b1 = list.stream()
    .allMatch((u) -> u.getAge() > 20);
boolean b2 = list.stream()
    .anyMatch((u) -> u.getAge() > 20);
boolean b3 = list.stream()
    .noneMatch((u) -> u.getAge() > 20);
System.out.println(b1 + "---" + b2 + "----" + b3);
Optional<User> f = list.stream()
    .findFirst();
if (f.isPresent()) {
    System.out.println(f.get());
}
long count = list.stream()
    .count();
list.stream()
    .max((u1, u2) -> Integer.compare(u1.getAge(), u2.getAge()));
  • 8 规约 reduce 提供处理数据的算法,和map结合,map是拿数据,拿到后reduce做处理
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
    .reduce(0, (x, y) -> x + y); // 求和
List<User> list1 = userDao.selectList(null);
Optional<Integer> sum1 = list1.stream()
    .map(User::getAge)
    .reduce(Integer::sum); // 求和
  • 9 收集 collect
    收集功能比较多样,Java8中提供了好多种结果,可以把指定数据收集为集合、map、set等类型,也可以收集为平均值、总数、统计个数等,都在java.util.stream.Collectors这个类的方法中。
List<User> list = userDao.selectList(null);
List<String> nameList = list.stream()
    .map(User::getName)
    .collect(Collectors.toList());
Map<Integer, User> userMap = list.stream()
    .collect(Collectors.toMap(user -> user.getId(), user -> user));
Long count = list.stream()
    .collect(Collectors.counting());
Double average = list.stream()
    .collect(Collectors.averagingInt(User::getAge));

4. Optional类

Optional 是个容器,它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 常用方法:

  • Optional.of(T value) -- 构建一个Optional实例,参数不能为空
  • Optional.empty() -- 构建空实例,不能有参
  • Optional.ofNullable(T value) -- T不为null,构建实例,T为null,构建空实例
  • Optional.isPresent()-- 判断是否有值
  • Optional.orElse(T other) -- 如果有值,返回值,没有返回T
  • Optional.orElseGet(Supplier s) -- 如果有值,返回值,没有返回s获取的值
  • Optional.map(Function f) -- 如果有值,返回f处理后的Optional,没有返回Optional.empty()

5.

串行流:单线程的方式操作; 数据量比较少的时候。
并行流:多线程方式操作;数据量比较大的时候,原理:
Fork join 将一个大的任务拆分n多个小的子任务并行执行,
最后在统计结果,有可能会非常消耗cpu的资源,确实可以
提高效率。

6. 新日期时间Api

  1. 时间戳
Instant now = Instant.now();
System.out.println(now);
System.out.println(now.atOffset(ZoneOffset.ofHours(8)));
System.out.println(now.atOffset(ZoneOffset.ofHours(8)).toEpochSecond());
  1. 时间 LocalDate LocalTime LocalDateTime
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
LocalDateTime ldt1 = LocalDateTime.of(2022, 3, 31, 11, 50, 20);
System.out.println(ldt1);
System.out.println(ldt.plusDays(1)); 
System.out.println(ldt.minusMonths(1));
System.out.println(ldt.getDayOfMonth());
  1. 时间差
Duration dur = Duration.between(ldt1, ldt);
System.out.println(dur.getSeconds());
System.out.println(dur.toMillis());
  1. 日期差
LocalDate ld1 = LocalDate.of(2020, 5, 31);
LocalDate ld2 = LocalDate.now();
Period pe = Period.between(ld1, ld2);
System.out.println(pe.getYears());
System.out.println(pe.getMonths());
System.out.println(pe.getDays());
  1. 时间日期修改更正
System.out.println(ldt.withDayOfMonth(1));
System.out.println(ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)));
  1. 格式化
ldt.format(DateTimeFormatter.ISO_DATE);
ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); System.out.println(ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  1. 时区 ZoneDate ZoneTime ZoneDateTime
// 所有时区
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
zoneIds.forEach(System.out::println);
System.out.println(ldt.atOffset(ZoneOffset.ofHours(8)));
System.out.println(ldt.atZone(ZoneId.of("Asia/Shanghai")));

相关文章

网友评论

      本文标题:java8新特性

      本文链接:https://www.haomeiwen.com/subject/drcrfktx.html