Saul's blog Saul's blog
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)

Saul.J.Wu

立身之本,不在高低。
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)
  • Java入门基础

  • Java核心基础

    • 多线程

    • Java常用类

    • 枚举类与注解

    • Java集合

    • 数据结构与算法

    • 泛型

    • IO流

    • 网络编程

    • 反射

    • 函数式编程

      • Lambda基础
      • 方法引用
      • JAVA8 新特性
        • JAVA8 新特性
          • Lambda 表达式
          • 函数式接口
          • 方法引用
          • 构造器引用 和 数组引用
          • Stream API
  • 设计模式

  • Web开发

  • SpringBoot

  • 微服务

  • Elasticsearch

  • 运维

  • 后端
  • Java核心基础
  • 函数式编程
SaulJWu
2021-07-06

JAVA8 新特性

[TOC]

# JAVA8 新特性

# Lambda 表达式

# 基本介绍

    JAVA8中引入了一个新的操作符 “->”,该操作符称为箭头操作符或 Lambda 操作符。
    
    箭头操作符将 Lambda 表达式拆分成两部分:
        左侧:Lambda 表达式的参数列表
        右侧:Lambda 表达式中所需执行的功能,即 Lambda 体

    注意事项:
        Lambda 表达式需要“函数式接口”的支持

1
2
3
4
5
6
7
8
9

# 语法格式

# 格式一:无参数,无返回值
// 语法:() -> System.out.println("Hello Lambda");

@Test
public void test1(){
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("Holle JAVA");        
            }
    };
    r.run();

    System.out.println("-----------------------------------");

    Runnable r1 = () -> System.out.println("Hello Lambda");
    r1.run();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 格式二:有参数,无返回值
/** 
* 格式二:有参数,无返回值 * (x) -> System.out.println(x); 
*/
@Test
public void test2(){   
    Consumer<String> con = new Consumer<String>() {
        @Override        
        public void accept(String s){
            System.out.println(s);
        }    
    };    
    
    con.accept("Hello JAVA");   
    
    System.out.println("---------------------------------");   

    Consumer<String> con1 = (x)-> System.out.println(x);

    con1.accept("Hello Lambda");
    
    System.out.println("---------------------------------");   

    // 若只有一个参数,小括号也可以省略不写
    Consumer<String> con1 = x -> System.out.println(x);

    con1.accept("Hello Lambda");
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 格式三:有两个或以上的参数,有返回值,并且Lambda 体中有多条语句
/**
* 格式三:有两个或以上的参数,有返回值,并且Lambda 体中有多条语句
*  Comparator<Integer> com = (x,y) -> {
*      System.out.println("Lambda...");
*      return Integer.compare(x,y);
*  };
*/
@Test
public void test3(){
  Comparator<Integer> com = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
      return Integer.compare(o1,o2);
    }
  };

  System.out.println("-------------------------------------");

  Comparator<Integer> com = (x,y) -> {
    System.out.println("Lambda...");
    return Integer.compare(x,y);
  };
  
  System.out.println("-------------------------------------");
  
  //如果Lambda 体中只有一条语句, 大括号和 return 都可以省略不写
  Comparator<Integer> com = (x,y) -> Integer.compare(x,y); 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 函数式接口

# 基本概念

   概念:只包含一个抽象方法的接口,称之为函数式接口
   
   可以通过 Lambda 表达式来创建该接口的对象。
       注意:如果 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明。
       
   可以在接口上使用 @FunctionalInterface 注解,如果没有报错,则证明该接口为函数式接口。
   
1
2
3
4
5
6
7

# 四大核心函数式接口

函数式接口 参数类型 返回类型 用途
Consumer 消费型接口 T void 对类型为T的对象应用操作
Supplier 供给型接口 无 T 返回类型为T的对象。
Function<T,R> 函数型接口 T R 对类型为T的对象应用操作,并返回结果,结果是R类型的对象。
Predicate 断定型接口 T boolean 确定类型为T的对象是否满某约束,并返回 boolean 值。
# 示例
@Test
void test3() {
   //旧
   consumerTest(200, new Consumer<Double>() {
       @Override
       public void accept(Double aDouble) {
           System.out.println("周末花了 " + aDouble + " 块");
       }
   });
   System.out.println("--------------------------------------------------");
   //Lambda
   consumerTest(200,money -> System.out.println("花超了 " + money + " 块"));
}

public void consumerTest(double money, Consumer<Double> con){
   con.accept(money);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 方法引用

# 基本概念

概念:
    当要传递给Lambda体的操作,已经有实现的方法了,就可以使用方法引用。
    
    方法引用可以看作是 Lambda 表达式深层次的表达。换句话说,方法引用就是 Lambda 表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是 Lambda 表达式的一个语法糖。
    
要求:
    实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
    
格式:
    使用操作符 “ :: ”将类(或对象)与方法名分隔开来。
    
三种使用情况:
    -> 对象 :: 非静态方法
    -> 类 :: 静态方法
    -> 类 :: 非静态方法

使用的要求:
    要求接口中的抽象方法上的形参列表和返回值类型 
        必须与方法引用的方法上的形参列表和返回值类型相同!(针对前两种使用情况)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 对象 :: 非静态方法

@Test
void test3() {
    Consumer<String> con1 = str -> System.out.println(str);
    con1.accept("芜湖");

    System.out.println("------------------------------------------");

    Consumer<String> con2 = System.out::println;
    con1.accept("起飞");
}

@Test
void test4() {
    PmsProduct ppt = new PmsProduct();
    ppt.setName("小明");

    Supplier<String> sup1 = () -> ppt.getName();
    System.out.println(sup1.get());

    System.out.println("---------------------------------------------");

    Supplier<String> sup2 = ppt::getName;
    System.out.println(sup2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 类 :: 静态方法


@Test
void test3() {
    Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
    System.out.println("com1 = " + com1.compare(1,2));

    System.out.println("-------------------------------------------");

    Comparator<Integer> com2 = Integer::compareTo;
    System.out.println("com2 = " + com2.compare(2,1));
}

1
2
3
4
5
6
7
8
9
10
11
12

# 类 :: 非静态方法


@Test
void test2() {
    Comparator<String> com1 = (t1,t2) -> t1.compareTo(t2);
    System.out.println(com1.compare("abc","aaa"));

    System.out.println("----------------------------");

    Comparator<String> com2 = String::compareTo;
    System.out.println(com2.compare("aaa","bbb"));
}

@Test
void test3() {
    BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
    System.out.println(pre1.test("aaa","bbb"));

    System.out.println("----------------------------------------");

    BiPredicate<String,String> pre2 = String::equals;
    System.out.println(pre2.test("aaa","aaa"));
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 构造器引用 和 数组引用

# 构造器引用基本使用

// 构造器引用和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。抽象方法的返回值类型即为构造器所属的类的类型。

@Test
void test3() {
    Supplier<PmsProduct> sup = new Supplier<PmsProduct>() {
        @Override
        public PmsProduct get() {
            return new PmsProduct();
        }
    };

    System.out.println("****************************************");

    Supplier<PmsProduct> sup1 = () -> new PmsProduct();

    System.out.println("****************************************");

    Supplier<PmsProduct> sup2 = PmsProduct::new;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 数组引用基本使用


@Test
void test3() {
    Function<Integer, String[]> fun1 = len -> new String[len];
    String[] arr1 = fun1.apply(5);
    System.out.println(arr1.length);

    Function<Integer, String[]> fun2 = String[]::new;
    String[] arr2 = fun2.apply(6);
    System.out.println("arr2 = " + arr2.length);
}
1
2
3
4
5
6
7
8
9
10
11

# Stream API

# 为什么要用 Stream API

    在目前的实际开发中,我们更多的数据处理,计算等都会放在 JAVA 层面去进行,而Stream API 就是为此而出现。
    
    Stream 是 java8 中用于处理集合、数组,包括 过滤,排序,遍历等操作。
    
    Stream 和 Collection 集合的区别:
        1、Collection 是一种静态的内存数据结构,而 Stream 则是有关计算的。
        2、Collection 主要面向内存,存储在内存中; Stream 则是面向 CPU,通过 CPU 实现计算。
    
1
2
3
4
5
6
7
8

# 什么是 Stream

Stream 是一种数据渠道,用于操作数据源(集合、数组等)所生成的元素序列

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

1
2
3
4
5
6
7

# Stream 的创建

# 创建方式一:集合
@Test
public void test4(){
    // 通过集合可以创建两种 Stream ,串行流和并行流

  // 拿到一个用于测试的集合,该集合中有 1~12 个数字
    List<Integer> numbers = StudentData.getNumbers();

    // 返回一个串行流,即有顺序的
    Stream<Integer> stream = numbers.stream();
    stream.forEach(a -> System.out.println(a));

    System.out.println("-------------------------------------------------------");

    // 返回一个并行流,即无序的
    Stream<Integer> bookStream = numbers.parallelStream();
    bookStream.forEach(b -> System.out.println(b));
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建方式二:数组
@Test
public void test5(){
  int[] arr = new int[] {1,2,3,4,5,6,7,8};
  //调用 Arrays 类的 Stream 返回一个流
  IntStream stream = Arrays.stream(arr);
}
1
2
3
4
5
6
# 创建方式三:通过 Stream 的 of()
@Test
public void test6(){
  Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
}
1
2
3
4
# 创建方式四:无限流
@Test
public void test7(){
   /**
	* public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
	* UnaryOperator:一个函数式接口,将传入的对象进行运算,并返回结果
	* 传入数字 1,迭代十个数。
	*/
  Stream.iterate(1,t -> t+1).limit(10).forEach(System.out::println);

  System.out.println("--------------------------------------------------");

  /**
   * public static<T> Stream<T> generate(Supplier<T> s)
   * 生成并打印十个随机数
   */
  Stream.generate(Math::random).limit(10).forEach(System.out::println);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 筛选

@Test
public void test8() {
  List<Integer> numbers = StudentData.getNumbers();
  //filter : 过滤,可以使用 Lambda 表达式,从流中过滤出符合条件的数据
  //        numbers.stream().filter(i -> i>7 ).forEach(System.out::println);

  // limit :截断流,使其元素不超过给定的数量
  //        numbers.stream().limit(3).forEach(System.out::println);

  // skip :跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流
  //        numbers.stream().skip(10).forEach(System.out::println);

  // distinct :筛选,通过流所生成元素的 hashCode 和 equals 两个方法,去除重复的元素

  //为了体现效果,增加三个重复元素到集合中
  numbers.add(10);
  numbers.add(11);
  numbers.add(12);

  numbers.stream().forEach(System.out::println);

  System.out.println("-------- 对比效果 ----------");

  numbers.stream().distinct().forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 映射

@Test
public void test9() {
  // map(Function f):接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  List<String> list = Arrays.asList("a", "b", "c", "d");

  list.stream().map(str -> {
    switch (str) {
      case "a":
        return 1;
      case "b":
        return 2;
      case "c":
        return 3;
      case "d":
        return 4;
      default:
        return 0;
    }
  }).forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 额外扩展:flatMap

@Test
public void test10(){
  // flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流。
  // flatMap 和 map 的区别
  List<String> list = Arrays.asList("aa", "bb", "cc", "dd");

  //map
  Stream<Stream<Character>> stream = list.stream().map(LambdaTest::fromStringToStream);
  stream.forEach(str -> {
    str.forEach(System.out::println);
  });

  System.out.println("------------------ 分割线 --------------------");

  //flatMap
  Stream<Character> stream1 = list.stream().flatMap(LambdaTest::fromStringToStream);
  stream1.forEach(System.out::println);

}

public static Stream<Character> fromStringToStream(String str){
  List<Character> list = new ArrayList<>();
  for (Character c : str.toCharArray()){
    list.add(c);
  }
  return list.stream();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 排序

@Test
public void test11() {
  // sorted :自然排序,只适用于基本元素的比较
  List<Integer> list = Arrays.asList(11,30,2,5,60,44,22);
  list.stream().sorted().forEach(System.out::println);

  System.out.println("-------------------- 分割线 -------------------");

  // sorted(Comparator<? super T> comparator):可以用于指定自己想要的排序
  List<Student> students = StudentData.getStudents();

  students.stream().sorted((a1, a2) -> Integer.compare(a1.getAge(),a2.getAge())).forEach(System.out::println);

  System.out.println("-------------------- 分割线 -------------------");
  //还可以这样写
  students.stream().sorted(Comparator.comparing(Student::getAge)).forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 查找与匹配

@Test
public void test12(){
  List<Integer> list = Arrays.asList(11,30,2,80,60,44,22);

  // allMatch:检查是否匹配所有元素。
  // 是否所有元素都大于 5
  boolean allMatch = list.stream().allMatch(a -> a > 5);
  System.out.println("allMatch = " + allMatch);

  // anyMatch:检查是否至少匹配一个元素
  // 是否存在大于 5 的元素
  boolean anyMatch = list.stream().anyMatch(a -> a > 5);
  System.out.println("anyMatch = " + anyMatch);

  // noneMatch:检查是否没有匹配的元素。没有就会返回 true ,有则返回 false
  // 是否存在数字 50
  boolean noneMatch = list.stream().noneMatch(a -> a == 50);
  System.out.println("noneMatch = " + noneMatch);

  // findFirst:返回第一个元素
  Optional<Integer> first = list.stream().findFirst();
  System.out.println("first = " + first);

  // findAny:返回当前流中的任意元素
  Optional<Integer> any = list.parallelStream().findAny();
  System.out.println("any = " + any);

  // count:返回流中元素的总个数
  // 查询大于 11 的元素有多少个
  long count = list.stream().filter(a -> a > 11).count();
  System.out.println("count = " + count);

  // max:返回流中最大值
  Optional<Integer> max = list.stream().max(Integer::compareTo);
  System.out.println("max = " + max);

  // min:返回流中最小值
  Optional<Integer> min = list.stream().min(Integer::compareTo);
  System.out.println("min = " + min);

  // forEach:内部迭代,说人话就是遍历
  list.stream().forEach(System.out::println);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# 归约(递归运算)

@Test
public void test13(){
  List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
  // reduce(T identity, BinaryOperator<T> accumulator):可以将流中元素反复结合起来,得到一个值并返回。
  //初始值为0.计算 1-10 的自然数的和
  Integer reduce = list.stream().reduce(0, Integer::sum);
  System.out.println("reduce = " + reduce);

  System.out.println("------------------ 分割线 -------------------");

  // reduce(BinaryOperator<T> accumulator)
  // 不设定初始值,计算所有元素的总和
  list.stream().reduce(Integer::sum);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 收集(转化)

@Test
public void test14() {
  List<Student> list = StudentData.getStudents();
  // collect(Collector c):将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法
  List<Integer> integerList = list.stream().map(Student::getAge).collect(Collectors.toList());
  System.out.println("Collectors.toList()");
  integerList.stream().forEach(System.out::println);

  System.out.println("-----------------------------------------------------------");

  Set<Integer> integerSet = list.stream().map(Student::getAge).collect(Collectors.toSet());
  System.out.println("Collectors.toSet()");
  integerSet.stream().forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
帮我改善此页面 (opens new window)
上次更新: 2021/07/06, 07:48:41
方法引用
概念

← 方法引用 概念→

最近更新
01
zabbix学习笔记二
02-28
02
zabbix学习笔记一
02-10
03
Linux访问不了github
12-08
更多文章>
Theme by Vdoing | Copyright © 2020-2022 Saul.J.Wu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式