Java8新特性--语法归纳

Java8语法归纳

  • Java8主要内容
  • Lambda表达式
    • Lambda初体验
    • Lambda表达式语法
    • Java8内置的四大核心函数式接口
    • 引用
  • Stream API
    • Stream流
    • Stream的三个操作步骤
      • 创建Stream
      • 中间操作
      • 终止操作
  • Optional类

前言:
JDK 8 正式版在 2013 年 9 月份发布,已近发布了很长一段时间了,工作中更多的时候使用的是java7.今天对java8做一个学习笔记式的整理,目的是担心长时间的未使用而忘记一些语法.详细的java8可参考 传送门

Java8主要内容

  1. Lambda表达式
  2. 函数式接口
  3. 方法引用与构造器引用
  4. Stream API
  5. 接口中的默认方法与静态方法
  6. 新时间日期API
  7. 其他新特性

Java8新特性简介:

  • 速度更快
  • 代码更少(增加了新的Lambda表达式)
  • 强大的Stream API
  • 便于并行
  • 最大化减少空指针异常Optional

注意:

此篇主要整理的式Lambda表达式与Stream API以及Optional

Lambda表达式

Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递). 可以写出更简洁,更灵活的代码. 作为一种更紧凑的代码风格, 使Java语言表达能力得到了提升.

Lambda初体验

在写代码的时候我们可能只需要return Integer.compare(o1,o2); Lambda表达式可以使代码更加简洁

import org.junit.Test;

import java.util.*;

/**
 * @Author 
 * @Date 
 **/
public class TestLambda {

    //匿名内部类的写法
    @Test
    public void test1(){
        Comparator<Integer> integerComparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        TreeSet<Integer> integerComparators = new TreeSet<>(integerComparator);
    }

    //Lambda表达式
    @Test
    public void test2(){
        Comparator<Integer> integerComparator = (x, y) -> Integer.compare(x, y);
        TreeSet<Integer> integerComparators = new TreeSet<>(integerComparator);
    }
    
}

Lambda表达式语法

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

基础语法:

  1. 语法格式一: 无参数, 无返回值
    () -> System.out.printIn(“Hello Lambda!”);
  2. 语法格式二:有一个参数,并且无返回值 此时参数的小括号可以不写
    x -> System.out.printIn(x);
  3. 语法格式三:有两个参数,有返回值,并且Lambda体中有多条语句
    (x, y) -> {多条语句}
  4. 语法格式四:若Lambda体中只有一条语句,return和大括号都可以省略不写
    Comparator com = (x,y) -> Integer.compare(x,y);
  5. 语法格式五:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即”类型推断“

Lambda表达式前提条件:

Lambda表达式需要"函数式接口"的支持

提示:
函数式接口: 接口中有且只有一个抽象方法的接口, 称为函数式接口. 可以使用注解@FunctionalInterface修饰, 可以检查是否是函数式接口.

Java8内置的四大核心函数式接口

  1. Consumer : 消费型接口
    void accept(T t);

  2. Supplied : 供给型接口
    T get();

  3. Function : 函数型接口
    R apply(T t);

  4. Predicate : 断言型接口
    boolean test(T, t);

举例说明:

/**
 * @Author 
 * @Date 
 **/

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;


public class TestLambda3 {

    //Consumer : 消费型接口
    @Test
    public void test1(){
        happy(1000, (m) -> System.out.println("消费:" + m + "元"));
    }

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

    //产生指定个随机数的集合
    @Test
    public void test2(){
        List<Integer> list = getNumList(10, () -> (int) (Math.random() * 100));
        for (Integer integer : list) {
            System.out.println(integer);
        }
    }

    public List<Integer> getNumList(int num, Supplier<Integer> supplier){
        List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < num; i++) {
            Integer integer = supplier.get();
            list.add(integer);
        }
        return list;
    }

}


说明:

上面的代码就很好的解释了我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)

引用

1. 方法引用:

若Lambda体中的内容已经有方法实现了, 我们就可以用"方法引用". 可以理解为方法引用时Lambda表达式的另外一种表现形式

主要有三种语法格式

  1. 对象::实例方法名
  2. 类::静态方法名
  3. 类::实例方法名 (非静态方法)

注意:

  1. Lambda体中的参数列表与返回值要和函数式接口抽象方法参数列表和返回值类型要一致
  2. 若Lambda参数列表中的第一个参数是实例方法的调用者,第二个参数是实例方法的参数时就可以使用ClassName::method

2. 构造器引用:

格式:

  • ClassName::new;

注意:

需要调用的构造器参数列表要和函数式接口抽象方法的参数列表保持一直

3. 数组引用:

格式:

  • Type[]::new;

上面三种类型的引用举例如下:

/**
 * @Author 
 * @Date 
 **/

import org.junit.Test;

import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class TestMethodRef {
    //数组引用
    @Test
    public void test5(){
        Function<Integer,String[]> fun = (x) -> new String[x];
        String[] apply = fun.apply(10);
        System.out.println(apply.length);

        Function<Integer,String[]> fun1 = String[]::new;
        String[] apply1 = fun1.apply(20);
        System.out.println(apply1.length);
    }

    //构造器引用
    @Test
    public void test4(){
        Supplier<Employee> sup = () -> new Employee();
        //构造器引用方式
        //这里使用的构造器也是根据Supplier接口中的抽象方法的参数进行匹配
        Supplier<Employee> sup1 = Employee::new;
        Employee employee = sup1.get();
        System.out.println(employee);

        //这里使用的构造器也是根据Function接口中的抽象方法的参数进行匹配
        Function<Integer, Employee> fun = (x) -> new Employee(x);
        Function<Integer, Employee> fun1 = Employee::new;
        Employee apply = fun1.apply(21);
        System.out.println(apply);
    }

    //类::实例方法名
    @Test
    public void test3(){
        BiPredicate<String, String> bp = (x, y) -> x.equals(y);
        //②若Lambda参数列表中的第一个参数是实例方法的调用者,第二个参数是实例方法的参数时就可以使用ClassName::method
        BiPredicate<String, String> bp1 = String::equals;
    }

    //类::静态方法名
    @Test
    public void test2(){
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        //①Lambda体中的参数列表与返回值要和函数式接口抽象方法参数列表和返回值类型要一致
        //也就是compare的参数列表和返回值和Comparator中的抽象方法的参数和返回值类型都是一致的
        Comparator<Integer> com1 = Integer::compare;
        int compare = com1.compare(12, 15);
        System.out.println(compare);
    }

    //对象::实例方法名
    @Test
    public void test1(){
        Consumer<String> con = (x) -> System.out.println(x);
        PrintStream out = System.out;
        Consumer<String> con1 = out::println;

    }
}

Employee类

import java.util.Objects;

/**
 * @Author 
 * @Date 
 **/
public class Employee {
    private String name;
    private int age;
    private double salary;

    public Employee(int age) {
        this.age = age;
    }

    public Employee() {
    }

    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                Double.compare(employee.salary, salary) == 0 &&
                Objects.equals(name, employee.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, salary);
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

Stream API

Java中有两个重要的改变. 一个是Lambda表达式, 另外一个则是Stream API(java.util.stream.*).
Stream是Java8中处理集合的关键抽象概念, 它可以指定你希望对集合进行的操作, 可以执行非常复杂的查找, 过滤和映射数据等操作. 使用Stream API对集合数据进行操作, 就类似于使用SQL执行的数据库查询. 也可以使用Stream API来并行执行操作. 简而言之,Stream API提供了一种高效且易于使用的处理数据的方式.

Stream流

流(Stream)是数据渠道, 用于操作数据源(集合, 数组等)所生成的元素序列. 集合讲的是数据, 流讲的是计算.

注意:

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

Stream的三个操作步骤

  1. 创建 Stream
    一个数据源(集合, 数组等)获取一个流
  2. 中间操作
    一个中间操作链(多个中间操作连接起来形成的一个流水线), 对数据员的数据进行处理. 除非流水线上发生终止操作,否则中间操作是不会执行任何的处理, 而在终端操作时一次性全部处理, 称为"惰性求值"
  3. 终端操作(终止操作)
    一个终止操作, 执行中间操作链, 并产生结果

创建Stream

创建流的四种方式:

  1. 通过Collection系列集合提供的stream()或者parallelStream() 并行的流
  2. 通过Arrays中的静态方法stream()获取数组流
  3. 通过Stream中的静态方法of()
  4. 创建无限流

举例说明:

/**
 * @Author 
 * @Date 
 **/

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class TestStreamApi1 {
    //创建Stream
    @Test
    public void test1(){
        //1,可以通过Collection系列集合提供的stream()或者parallelStream() 并行的流
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
        //2,通过Arrays中的静态方法stream()获取数组流
        Employee[] employees = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(employees);
        //3,通过Stream中的静态方法of()
        Stream<String> stream3 = Stream.of("aaa", "bbb", "cc");
        //4,创建无限流
            //4.1迭代
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return x + 2;
        });
        stream4.forEach(System.out::println);
            //4.2生成
        Stream<Double> stream5 = Stream.generate(Math::random);
        stream5.forEach(System.out::println);
    }

}

说明:

Employee类在上面的示例代码中有

中间操作

方法 说明
筛选与切片
filter() 接收Lambda,从流中排除某些元素
limit() 截断流,使其元素不超过给定的数量
skip(n) 跳过元素,返回一个丢掉前n个元素的流.若流中元素不足n个,则返回一个空流.与limit()互补
distinct() 筛选,通过流所生成的元素的hashCode()和equals()方法去除重复元素
映射
map() 1. 接收Lambda,将元素转换为其他形式或提取信息. 2. 接收一个函数作为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素
flatMap() 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流连接成一个流
排序
sorted() 自然排序(comparable)
sorted(Comparator com) 定制排序(comparator)
查找与匹配
allMatch() 检查是否匹配所有元素
anyMatch() 检查是否至少匹配一个元素
noneMatch() 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中任意元素
count() 返回流中元素的综合
max() 返回流中的最大值
min() 返回流中的最小值

终止操作

方法 说明
归约
reduce(T identity, BinaryOperator accumulator) 可以将流中的元素反复结合起来,得到一个新值
reduce(BinaryOperator accumulator) 可以将流中的元素反复结合起来,得到一个新值
收集
collect() 将流转换为其他形式. 接收一个collector接口的实现,用于给Stream中的元素做汇总的方法

以上方法的部分示例代码:

/**
 * @Author 
 * @Date 
 **/

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;

public class TestStreamApi2 {

    List<Employee> employees = Arrays.asList(
            new Employee("张三",18,9999.9),
            new Employee("李四",38,5555.5),
            new Employee("王五",50,6666.6),
            new Employee("王五",50,6666.6),
            new Employee("王五",50,6666.6),
            new Employee("赵六",16,3333.3),
            new Employee("田七",8,7777.7)
    );

    //中间操作

    /**
     * 排序
     * sorted() ----自然排序(comparable)
     * sorted(Comparator com) ----定制排序(comparator)
     */

    @Test
    public void test7(){
        List<String> list = Arrays.asList("aaa","ddd","rrr","ccc");
        list.stream()
                .sorted()
                .forEach(System.out::println);

        employees.stream()
                .sorted((e1,e2) -> {
                    if (e1.getAge() == e2.getAge()){
                        return e1.getName().compareTo(e2.getName());
                    }else {
                        return e1.getName().compareTo(e2.getName());
                    }
                }).forEach(System.out::println);
    }


    /**
     * 映射
     * map  ----接收Lambda,将元素转换为其他形式或提取信息.接受一个函数作为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素
     * flatMap  ----接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流连接成一个流
     *
     * map和flatMap的操作类似于list的add(Object obj)  和  addAll(collection coll)
     *
     */
    @Test
    public void test5(){
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
        list.stream().map(String::toUpperCase)
                .forEach(System.out::println);

        System.out.println("======================================");
        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
        System.out.println("======================================");

    }

    @Test
    public void test6(){
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
        list.stream()
                .flatMap(TestStreamApi2::filterCharcter)
                .forEach(System.out::println);
    }

    public static Stream<Character> filterCharcter(String str){
        List<Character> list = new ArrayList<>();
        for (char ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }


    /**
     * 筛选与切片
     * filter  ----接收Lambda,从流中排除某些元素
     * limit  ----截断流,使其元素不超过给定数量
     * skip(n)  ----跳过元素,返回一个丢掉了前n个元素的流。若流中元素不足n个,则返回一个空流,与limit(n)互补
     * distinct  ----筛选,通过流所生成的元素的hashCode()和equals()去除重复元素
     */

    @Test
    public void test4(){
        employees.stream()
                .filter((x) -> x.getSalary() > 5000)
                .skip(2)
                .distinct()
                .forEach(System.out::println);
    }

    @Test
    public void test3(){
        employees.stream()
                .filter((x) -> {
                    System.out.println("短路!");
                    return x.getSalary() > 5000;
                })
                .limit(2)//获取到2条结果后就结束循环,有&&  || 的效果
                .forEach(System.out::println);
    }

    //内部迭代:迭代由Stream API完成
    @Test
    public void test1(){
        employees.stream()
                //中间操作:不会执行任何操作
                .filter((x) -> {
                    System.out.println("filter的中间操作!");
                    return  x.getAge() >= 35;
                })
                //终止操作:一次性执行全部操作,即"惰性求值"
                .forEach(System.out::println);
    }

    //外部迭代
    @Test
    public void test2(){
        Iterator<Employee> iterator = employees.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }


}

/**
 * @Author 
 * @Date 
 **/

import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;

public class TestStreamApi3 {
    //终止操作

    List<Employee> employees = Arrays.asList(
            new Employee("张三",18,9999.9),
            new Employee("李四",38,5555.5),
            new Employee("王五",50,6666.6),
            new Employee("王五",50,6666.6),
            new Employee("王五",50,6666.6),
            new Employee("赵六",16,3333.3),
            new Employee("田七",8,7777.7)
    );

    /**
     * 收集
     * collect  ----将流转换为其他形式.接收一个collector接口的实现,用于给Stream中元素做汇总的方法
     */

    @Test
    public void test03(){
        List<String> list = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        list.forEach(System.out::println);
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

        Set<String> set = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());//去重
        set.forEach(System.out::println);
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        HashSet<String> hashSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        hashSet.forEach(System.out::println);
    }


    /**
     * 规约
     * reduce(T identity, BinaryOperator) / reduce(BinaryOperator)  ---- 可以将流中的元素反复结合起来,得到一个新值
     */

    @Test
    public void test02(){
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream()
                .reduce(0, Integer::sum);
        System.out.println(sum);

        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        Optional<Double> sarlySum = employees.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(sarlySum.get());
    }


    /**
     * 查找和匹配
     * allMatch  ----检查是否匹配所有元素
     * anyMatch  ----检查是否至少匹配一个元素
     * noneMatch  ----检查是否没有匹配所有元素
     * findFirst  ----返回第一个元素
     * findAny  ----返回当前流中的任意元素
     * count  ----返回流中元素的综合
     * max  ----返回流中的最大值
     * min  ----返回流中的最小值
     */



    @Test
    public void test(){
        boolean b = employees.stream()
                .noneMatch((e) -> e.getName().equals("郑八"));
        System.out.println(b);//true
    }
}

Optional类

Optional类(java.util.Optional)是一个容器类, 代表一个值存在或不存在. 原来用null表示一个值不存在, 现在Optional可以更好的表达这个概念.并且可以避免空指针异常.

常用方法:

方法名 说明
Optional.of(T t) 创建一个Optional实例
Optional.empty() 创建一个空的Optional实例
Optional.ofNullable(T t) 若 t 不为null, 创建Optional实例, 否则创建空Optional实例
isPresent() 判断是否包含值
orElse(T t) 如果调用对象包含值, 返回该值,否则返回t
orELseGet(Supplier s) 如果调用对象包含值,返回该值,否则返回s获取的值
map(Function f) 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
flatMap(Function f) 与map类似,要求返回值必须是Optional

简单示例如下:

import org.junit.Test;

import java.util.ArrayList;
import java.util.Optional;

/**
 * @Author 
 * @Date 
 **/
public class OptionalTest1 {

    /**
     * Optional.of(T t) -- 创建一个Optional实例
     * Optional.empty() -- 创建一个空的Optional实例
     * Optional.ofNullable(T t) -- 若 t 不为null, 创建Optional实例, 否则创建空Optional实例
     * isPresent()
     */
    @Test
    public void test1(){
        ArrayList<String> arr1 = new ArrayList<>();
        ArrayList<String> arr2 = new ArrayList<>();
        Optional<ArrayList<String>> opt1 = Optional.ofNullable(null);
        Optional<ArrayList<String>> opt2 = Optional.ofNullable(arr2);
        arr1.add("Hello");
        arr1.add("world");
        Optional<ArrayList<String>> opt3 = Optional.of(arr1);
        Optional<Object> opt4 = Optional.empty();
        System.out.println(opt1.isPresent());
        System.out.println(opt2.isPresent());
        System.out.println(opt3.isPresent());
        System.out.println(opt4.isPresent());

    }

    /**
     * orElse(T t)和orElseGet(Supplier s)的区别
     * 调用对象为空时,返回的都是后面参数的值
     * 若不为空,orElse(T t)还是会执参数中的代码,并产生了调用方法中的对象,而orElseGet(Supplier s)只会返回调用对象中的值,并不会产生对象
     */
    @Test
    public void test2(){
        ArrayList<String> arr1 = new ArrayList<>();
        ArrayList<String> arr2 = new ArrayList<>();
        Optional<ArrayList<String>> opt1 = Optional.ofNullable(null);
        Optional<ArrayList<String>> opt2 = Optional.ofNullable(arr2);
        arr1.add("Hello");
        arr1.add("world");
        Optional<ArrayList<String>> opt3 = Optional.of(arr1);
        Optional<Object> opt4 = Optional.empty();
        System.out.println(opt1.orElse(getDefaultValue()));
        System.out.println(opt1.orElseGet(this::getDefaultValue));
        System.out.println("===========================================");
        System.out.println(opt3.orElse(getDefaultValue()));
        System.out.println(opt3.orElseGet(this::getDefaultValue));

    }

    public ArrayList<String> getDefaultValue(){
        System.out.println("我被调用了");
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("我是默认值");
        return arrayList;
    }

    /**
     * map(Function f)
     * flatMap(Function f)
     */
    @Test
    public void test3(){
        ArrayList<String> arr1 = new ArrayList<>();
        ArrayList<String> arr2 = new ArrayList<>();
        Optional<ArrayList<String>> opt1 = Optional.ofNullable(null);
        Optional<ArrayList<String>> opt2 = Optional.ofNullable(arr2);
        arr1.add("Hello");
        arr1.add("world");
        Optional<ArrayList<String>> opt3 = Optional.of(arr1);
        Optional<Object> opt4 = Optional.empty();

        Optional<String> s = opt3.map((x) -> x.get(1));
        System.out.println(s.orElse("没有值"));
        System.out.println("=============================");
        Optional<String> s1 = opt3.flatMap((x) -> Optional.ofNullable(null));
        System.out.println(s1.orElse("没有值"));
    }

}

至此,Java8的部分新特性整理完毕!

你可能感兴趣的:(Java,java,开发语言)