Java8新特性

目录

      • 1. Java8新特性_简介
      • 2. 为什么使用 Lambda 表达式
        • 1-Lambda表达式
      • 3. Java8新特性_Lambda 基础语法
        • 2-函数式接口
      • 4. Java8新特性_Lambda 练习
      • 5. Java8新特性_四大内置核心函数式接口
      • 6. Java8新特性_方法引用与构造器引用
        • 3-方法引用与构造器引用
      • 7. Java8新特性_创建 Stream
        • 4-强大的 Stream API
      • 8. Java8新特性_Stream_筛选与切片
      • 9. Java8新特性_Stream_映射
      • 10. Java8新特性_Stream_排序
      • 11. Java8新特性_Stream_查找与匹配
      • 12. Java8新特性_Stream_归约与收集
      • 13. Java8新特性_Stream API 练习
      • 14. Java8新特性_并行流与串行流
        • 并行流与串行流
        • Fork/Join 框架
      • 15. Java8新特性_Optional 容器类
      • 16. Java8新特性_接口中的默认方法与静态方法
        • 6-接口中的默认方法与静态方法
      • 17. Java8新特性_传统时间格式化的线程安全问题
        • 5-新时间日期 API
      • 18. Java8新特性_新时间与日期 API-本地时间与时间戳
      • 19. Java8新特性_新时间和日期 API-时间校正器
      • 20. Java8新特性_新时间和日期 API-时间格式化与时区的处理
      • 21. Java8新特性_重复注解与类型注解
        • 重复注解与类型注解

1. Java8新特性_简介

HashMap在jdk1.7和jdk1.8中区别

jdk1.7:数组+链表

jdk1.8:数组+链表+红黑树
当哈希碰撞即链表上个数大于8并且总容量个数大于64,链表转成红黑树

红黑树除了添加以外,其他(查询、删除)效率都比链表高
扩容后重排序,不用重新运算hashCode值,直接找对应元素在原来哈希表的总长度加上它当前所在哈希表的位置

HashSet,同理

ConcurrentHashMap

jdk1.7
并发级别:16
锁+段机制:默认16段,每段默认16表
段太多浪费空间,段太少导致每段元素过多效率遍地

jdk1.8
CAS
无锁算法
CAS是底层操作系统支持的算法,效率高

Java 8新特性简介

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

2. 为什么使用 Lambda 表达式

1-Lambda表达式

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

package day01.com.lm.java8;

import java.util.Objects;

public class Employee {
     
    private int id;
    private String name;
    private Integer age;
    private double salary;
    private Status status;

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

    public Employee() {
     
        super();
    }

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

    public Employee(String name, Integer 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 Integer getAge() {
     
        return age;
    }

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

    public double getSalary() {
     
        return salary;
    }

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

    public Status getStatus() {
     
        return status;
    }

    public void setStatus(Status status) {
     
        this.status = status;
    }

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

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

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

    public enum Status {
     
        FREE,
        BUSY,
        VOCATION
    }
}
package day01.com.lm.java8;

public interface MyPredicate<T> {
     

    public boolean test(T t);
}
package day01.com.lm.java8;

public class FilterEmployeeByAge implements MyPredicate<Employee> {
     

    @Override
    public boolean test(Employee t) {
     
        return t.getAge() >= 35;
    }
}
package day01.com.lm.java8;

public class FilterEmployeeBySalary implements MyPredicate<Employee> {
     
    @Override
    public boolean test(Employee t) {
     
        return t.getSalary() >= 5000;
    }
}
package day01.com.lm.java8;

import org.junit.Test;

import java.util.*;

public class TestLambda1 {
     

    //原来的匿名内部类
    public void test1() {
     
        Comparator<Integer> com = new Comparator<Integer>() {
     
            @Override
            public int compare(Integer o1, Integer o2) {
     
                return Integer.compare(o1, o2);
            }
        };

        TreeSet<Integer> ts = new TreeSet<>(com);
    }

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

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 50, 6666.66),
            new Employee("赵六", 16, 3333.33),
            new Employee("田七", 8, 7777.77)
    );

    //需求:获取当前公司中员工年龄大于35的员工信息
    @Test
    public void test3() {
     
        List<Employee> list = filterEmployees(this.employees);

        for (Employee employee : list) {
     
            System.out.println(employee);
        }
    }

    public List<Employee> filterEmployees(List<Employee> list) {
     
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
     
            if (emp.getAge() >= 35) {
     
                emps.add(emp);
            }
        }
        return emps;
    }

    //需求:获取当前公司中员工工资大于5000的员工信息
    public List<Employee> filterEmployees2(List<Employee> list) {
     
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
     
            if (emp.getSalary() >= 5000) {
     
                emps.add(emp);
            }
        }
        return emps;
    }

    //优化方式一:策略设计模式
    @Test
    public void test4() {
     
        List<Employee> list = filterEmployee(employees, new FilterEmployeeByAge());

        for (Employee employee : list) {
     
            System.out.println(employee);
        }

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

        List<Employee> list2 = filterEmployee(employees, new FilterEmployeeBySalary());

        for (Employee employee : list2) {
     
            System.out.println(employee);
        }
    }

    public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> mp) {
     
        List<Employee> emps = new ArrayList<>();
        for (Employee employee : list) {
     
            if (mp.test(employee)) {
     
                emps.add(employee);
            }
        }
        return emps;
    }

    //优化方式二:匿名内部类
    @Test
    public void test5() {
     
        List<Employee> list = filterEmployee(employees, new MyPredicate<Employee>() {
     
            @Override
            public boolean test(Employee t) {
     
                return t.getSalary() <= 5000;
            }
        });

        for (Employee employee : list) {
     
            System.out.println(employee);
        }
    }

    //优化方式三:Lambda表达式
    @Test
    public void test6() {
     
        List<Employee> list = filterEmployee(employees, (e) -> e.getSalary() <= 5000);
        list.forEach(System.out::println);
    }

    //优化方式四:Stream API
    @Test
    public void test7() {
     
        employees.stream()
                .filter((e) -> e.getSalary() >= 5000)
                .limit(2)
                .forEach(System.out::println);

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

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

3. Java8新特性_Lambda 基础语法

从匿名类到 Lambda 的转换
Java8新特性_第1张图片
从原来使用匿名内部类作为参数传递到Lambda表达式作为参数传递
Java8新特性_第2张图片
Lambda 表达式语法
Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称
为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:
左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
Java8新特性_第3张图片
Java8新特性_第4张图片
类型推断
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”

2-函数式接口

什么是函数式接口

  • 只包含一个抽象方法的接口,称为函数式接口
  • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
  • 我们可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

自定义函数式接口
Java8新特性_第5张图片

package day01.com.lm.java8;

import org.junit.Test;

import java.util.*;
import java.util.function.Consumer;

/**
 * 一、Lambda表达式的基础语法:Java8中引入了一个新的操作符“->” 该操作符称为箭头操作符或Lambda操作符
 *                             箭头操作符将Lambda表达式拆分成两部分:
 * 左侧:Lambda表达式的参数列表
 * 右侧:Lambda表达式中所需要执行的功能,即Lambda体
 *
 * 语法格式一:无参数,无返回值
 *      () -> System.out.println("Hello Lambda!");
 *
 * 语法格式二:有一个参数,并且无返回值
 *      (x) -> System.out.println(x);
 *
 * 语法格式三:只有一个参数,小括号可以省略不写
 *      x -> System.out.println(x);
 *
 * 语法格式四:有两个以上的参数,有返回值,并且Lambda体有多条语句
 *        Comparator com = (x, y) -> {
 *             System.out.println("函数式接口");
 *             return Integer.compare(x, y);
 *         };
 *
 * 语法格式五:若Lambda体中只有一条语句,return和大括号都可以省略不写
 *      Comparator com = (x, y) -> Integer.compare(x, y);
 *
 * 语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即“类型推断”
 *      Comparator com = (Integer x, Integer y) -> Integer.compare(x, y);
 *
 * 上联:左右遇一括号省
 * 下联:左侧推断类型省
 * 横批:能省则省
 *
 * 二、Lambda表达式需要“函数式接口”的支持
 * 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。
 *             可以使用注解@FunctionalInterface修饰可以检查是否是函数式接口
 */
public class TestLambda2 {
     

    @Test
    public void test1() {
     //无参数,无返回值
        int num = 0;//jdk1.7前,必须加final

        Runnable r = new Runnable() {
     
            @Override
            public void run() {
     
                System.out.println("Hello World!" + num);
            }
        };

        r.run();

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

        Runnable r1 = () -> System.out.println("Hello World!" + num);
        r1.run();
    }

    @Test
    public void test2() {
     //有一个参数,并且无返回值
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("民哥威武!");
        //只有一个参数,小括号可以省略不写
        Consumer<String> con2 = x -> System.out.println(x);
        con2.accept("民哥威武!");
    }

    @Test
    public void test3() {
     //有两个以上的参数,有返回值,并且Lambda体有多条语句
        Comparator<Integer> com = (x, y) -> {
     
            System.out.println("函数式接口");
            return Integer.compare(x, y);
        };
    }

    @Test
    public void test4() {
     //若Lambda体中只有一条语句,return和大括号都可以省略不写
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        Comparator<Integer> com2 = (Integer x, Integer y) -> Integer.compare(x, y);
    }

    @Test
    public void test5() {
     
        String[] strs = {
     "aaa", "bbb", "ccc"};
//        String[] strs;
//        strs = {"aaa", "bbb", "ccc"};

        List<String> list = new ArrayList<>();

        show(new HashMap<>());
    }

    public void show(Map<String, Integer> map) {
     

    }

    //需求:对一个数进行运算
    @Test
    public void test6() {
     
        Integer num = operation(100, (x) -> x * x);
        System.out.println(num);

        System.out.println(operation(200, (y) -> y + 200));
    }

    public Integer operation(Integer num, MyFun mf) {
     
        return mf.getValue(num);
    }
}
package day01.com.lm.java8;

@FunctionalInterface
public interface MyFun<T> {
     

    public Integer getValue(Integer num);
}

4. Java8新特性_Lambda 练习

作为参数传递Lambda表达式
Java8新特性_第6张图片
作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收 Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

1.调用 Collections.sort() 方法,通过定制排序比较两个 Employee(先按年龄比,年龄相同按姓名比),使用 Lambda 作为参数传递。
2.
① 声明函数式接口,接口中声明抽象方法, public String getvalue(String str);
② 声明类 Testlambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值。
③ 再将一个字符串的第2个和第4个索引位置进行截取子串。
3.
① 声明一个带两个泛型的函数式接口,泛型类型为 T为参数,R为返回值
② 接口中声明对应抽象方法
③ 在 TestLambda类中声明方法,使用接口作为参数,计算两个 long 型参数的和。
④ 再计算两个 |ong 型参数的乘积。

package day01.com.lm.exer;

@FunctionalInterface
public interface MyFunction {
     
    public String getValue(String str);
}
package day01.com.lm.exer;

@FunctionalInterface
public interface MyFunction2<T, R> {
     
    public R getValue(T t1, T t2);
}
package day01.com.lm.exer;

import day01.com.lm.java8.Employee;
import org.junit.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class TestLambda {
     

    List<Employee> emps = Arrays.asList(
            new Employee("张三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 50, 6666.66),
            new Employee("赵六", 16, 3333.33),
            new Employee("田七", 8, 7777.77)
    );

    @Test
    public void test1() {
     
        Collections.sort(emps, (e1, e2) -> {
     
            if(e1.getAge() == e2.getAge()) {
     
                return e1.getName().compareTo(e2.getName());
            } else {
     
                return -Integer.compare(e1.getAge(), e2.getAge());
            }
        });

        for (Employee emp : emps) {
     
            System.out.println(emp);
        }
    }

    //需求:用于处理字符串
    public String strHandler(String str, MyFunction mf) {
     
        return mf.getValue(str);
    }

    @Test
    public void test2() {
     
        String trimStr = strHandler("\t\t I Love China! \t  ", str -> str.trim());
        System.out.println(trimStr);

        String upperStr = strHandler("I Love China!", str -> str.toUpperCase());
        System.out.println(upperStr);

        String substringStr = strHandler("I Love China!", str -> str.substring(2, 6));
        System.out.println(substringStr);
    }

    //需求:对于两个Long型数据进行处理
    public Long longHandler(Long l1, Long l2, MyFunction2<Long, Long> mf) {
     
        return mf.getValue(l1, l2);
    }

    @Test
    public void test3() {
     
        Long addLong = longHandler(100L, 200L, (l1, l2) -> l1 + l2);
        System.out.println(addLong);

        Long minusLong = longHandler(100L, 200L, (l1, l2) -> l1 * l2);
        System.out.println(minusLong);
    }
}

5. Java8新特性_四大内置核心函数式接口

Java 内置四大核心函数式接口

函数式接口 参数类型 返回类型 用途
Consumer
消费型接口
T void 对类型为T的对象应用操作,包含方法:
void accept(T t);
Supplier
供给型接口
T 返回类型为T的对象,包含方法:
T get();
Function
函数型接口
T R 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:
R apply(T t);
Predicate
断定型接口
T boolean 确定类型为T的对象是否满足某约束,并返回 boolean 值。包含方法:
boolean test(T t);

其他接口

函数式接口 参数类型 返回类型 用途
BiFunction
T, U R 对类型为T,U参数应用操作,返回R类型的结果。包含方法为:
R apply(T t, U u);
UnaryOperator
(Function子接口)
T T 对类型为T的对象进行一元运算,并返回T类型的结果,包含方法为:
T apply(T t);
BinaryOperator
(BiFunction子接口)
T, T T 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为:
T apply(T t1, T t2);
BiConsumer T, U void 对类型为T,U参数应用操作。包含方法为:
void accept(T t, U u);
ToIntFunction
ToLongFunction
ToDoubleFunction
T int
long
double
分别计算int、long、double值的函数
IntFunction
LongFunction
DoubleFunction
int
long
double
R 参数分别为int、long、double类型的函数
package day01.com.lm.java8;

import org.junit.Test;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Java内置的四大核心函数式接口
 *
 * Consumer:消费型接口
 *      void accept(T t);
 *
 * Supplier:供给型接口
 *      T get();
 *
 * Function:函数型接口
 *      R apply(T t);
 *
 * Predicate:断言型接口
 *      boolean test(T t);
 */
public class TestLambda3 {
     

    //Consumer 消费型接口:
    @Test
    public void test1() {
     
        happy(10000, (m) -> System.out.println("民哥喜欢大宝剑,每次消费" + m + "元"));
    }

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

    //Supper 供给型接口:
    @Test
    public void test2() {
     
        List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));

        for (Integer num : numList) {
     
            System.out.println(num);
        }
    }

    //需求:产生指定个数的整数,并放入集合中
    public List<Integer> getNumList(int num, Supplier<Integer> sup) {
     
        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < num; i++) {
     
            Integer n = sup.get();
            list.add(n);
        }

        return list;
    }

    //Function 函数型接口:
    @Test
    public void test3() {
     
        String newStr = strHandler("\t\t I Love China!  \t", (str) -> str.trim());
        System.out.println(newStr);

        String subStr = strHandler("I Love China!", (str) -> str.substring(2, 6));
        System.out.println(subStr);
    }

    //需求:用于处理字符串
    public String strHandler(String str, Function<String, String> fun) {
     
        return fun.apply(str);
    }

    //Predicate 断言型接口:
    @Test
    public void test4() {
     
        List<String> list = Arrays.asList("Hello", "I", "Love", "China");
        List<String> strList = filterStr(list, (s) -> s.length() > 4);

        for (String str : strList) {
     
            System.out.println(str);
        }
    }

    //需求:将满足条件的字符串放入集合中去
    public List<String> filterStr(List<String> list, Predicate<String> pre) {
     
        List<String> strList = new ArrayList<>();

        for (String str : list) {
     
            if (pre.test(str)) {
     
                strList.add(str);
            }
        }

        return strList;
    }
}

6. Java8新特性_方法引用与构造器引用

3-方法引用与构造器引用

方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!
方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。
如下三种主要使用情况:

  • 对象::实例方法
  • 类::静态方法
  • 类::实例方法

Java8新特性_第7张图片
Java8新特性_第8张图片
注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时:ClassName::methodName

构造器引用

格式: ClassName::new
与函数式接口相结合,自动与函数式接口中方法兼容。
可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!
Java8新特性_第9张图片
数组引用

格式: type[] :: new
Java8新特性_第10张图片

package day01.com.lm.java8;

import org.junit.Test;

import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.*;

/**
 * 一、方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”
 *          (可以理解方法引用是Lambda表达式的另外一种表现形式)
 *
 * 主要有三种语法格式:
 *
 * 对象::实例方法名
 *
 * 类::静态方法名
 *
 * 类::实例方法名
 *
 * 注意:
 *  ①Lambda体中调用方法的参数和返回值类型,要与函数式接口中抽象方法的函数列表和返回值保持一致!
 *  ②若Lambda参数列表中的第一参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用 ClassName :: method
 *
 * 二、构造器引用:
 *
 *  格式:
 *
 *  ClassName::new
 *
 *  注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表一致
 *
 * 三、数组引用
 *
 *  Type::new
 */
public class TestMethodRef {
     

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

        PrintStream ps = System.out;
        Consumer<String> con1 = ps::println;
        con1.accept(abcdef);

        Consumer<String> con2 = System.out::println;
        con2.accept(abcdef);
    }

    @Test
    public void test2() {
     
        Employee emp = new Employee();
        Supplier<String> sup = () -> emp.getName();
        String str = sup.get();
        System.out.println(str);

        Supplier<Integer> sup2 = emp::getAge;
        Integer num = sup2.get();
        System.out.println(num);
    }

    //类::静态方法名
    @Test
    public void test3() {
     
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        System.out.println(com.compare(6, 7));

        Comparator<Integer> com1 = Integer::compare;
        System.out.println(com1.compare(6, 7));
    }

    //类::实例方法名
    @Test
    public void test4() {
     
        BiPredicate<String, String> bp = (x, y) -> x.equals(y);
        System.out.println(bp.test("abc", "abc"));

        BiPredicate<String, String> bp2 = String::equals;
        System.out.println(bp2.test("abc", "abc"));
        System.out.println(bp2.test("abc", null));
//        System.out.println(bp2.test(null, "abc"));//空指针异常
    }

    //构造器引用
    @Test
    public void test5() {
     
        Supplier<Employee> sup = () -> new Employee();

        //构造器引用方式
        Supplier<Employee> sup2 = Employee::new;
        Employee emp = sup2.get();
        System.out.println(emp);
    }

    @Test
    public void test6() {
     
        Function<Integer, Employee> fun = (x) -> new Employee(x);

        Function<Integer, Employee> fun2 = Employee::new;
        Employee emp = fun2.apply(101);
        System.out.println(emp);

//        BiFunction bf = Employee::new;//Employee没有传2个参数的构造器,所以不可引用
    }

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

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

7. Java8新特性_创建 Stream

4-强大的 Stream API

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

什么是 Stream
流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”

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

  • 创建 Stream
    一个数据源(如:集合、数组),获取一个流
  • 中间操作
    一个中间操作链,对数据源的数据进行处理
  • 终止操作(终端操作)
    一个终止操作,执行中间操作链,并产生结果

Java8新特性_第12张图片
创建 Stream

  • 从Collection 获取流
    Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
    default Stream stream(): 返回一个顺序流
    default Stream parallelStream(): 返回一个并行流

  • 由数组创建流
    Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
    static Stream stream(T[] array): 返回一个流
    重载形式,能够处理对应基本类型的数组:
    public static IntStream stream(int[] array)
    public static LongStream stream(long[] array)
    public static DoubleStream stream(double[] array)

  • 由值创建流
    可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数。
    public static Stream of(T... values): 返回一个流

  • 由函数创建流:创建无限流
    可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。

    • 迭代
      public static Stream iterate(final T seed, final UnaryOperator f)
    • 生成
      public static Stream generate(Supplier s)
package day02.com.lm.java8;

import java.util.Objects;

public class Employee {
     
    private int id;
    private String name;
    private Integer age;
    private double salary;
    private Status status;

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

    public Employee() {
     
        super();
    }

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

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

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

    public String getName() {
     
        return name;
    }

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

    public Integer getAge() {
     
        return age;
    }

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

    public double getSalary() {
     
        return salary;
    }

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

    public Status getStatus() {
     
        return status;
    }

    public void setStatus(Status status) {
     
        this.status = status;
    }

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

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

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

    public enum Status {
     
        FREE,
        BUSY,
        VOCATION
    }
}
package day02.com.lm.java8;

import org.junit.Test;

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

/**
 * 一、Stream的三个操作步骤
 *
 * 1、创建Stream
 *
 * 2、中间操作
 *
 * 3、终止操作(终端操作)
 */
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[] emps = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(emps);

        //3、通过 Stream 类中的静态方法 of()
        Stream<String> stream3 = Stream.of("aa", "bb", "cc");

        //4、创建无限流
        //迭代
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        stream4.limit(10).forEach(System.out::println);

        //生成
        Stream.generate(() -> Math.random())
                .limit(5)
                .forEach(System.out::println);
    }
}

8. Java8新特性_Stream_筛选与切片

9. Java8新特性_Stream_映射

10. Java8新特性_Stream_排序

Stream 的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”

  • 筛选与切片
方 法 描 述
filter(Predicate p) 接收 Lambda ,从流中排除某些元素。
distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量。
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
  • 映射
方 法 描 述
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
  • 排序
方 法 描 述
sorted() 产生一个新流,其中按自然顺序排序
sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序
package day02.com.lm.java8;

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;

/**
 * 一、Stream的三个操作步骤
 *
 * 1、创建Stream
 *
 * 2、中间操作
 *
 * 3、终止操作(终端操作)
 */
public class TestStreamAPI2 {
     

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 50, 6666.66),
            new Employee("赵六", 16, 3333.33),
            new Employee("田七", 12, 8888.88),
            new Employee("田七", 12, 8888.88),
            new Employee("田七", 12, 8888.88)
    );

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

    //内部迭代:迭代操作由 Stream API 完成
    @Test
    public void test1() {
     
        //中间操作:不会执行任何操作
        Stream<Employee> stream = employees.stream()
                                        .filter((e) -> {
     
                                            System.out.println("Stream API 的中间操作");//可以看出,如果没有终止操作,中间操作不会执行
                                            return e.getAge() > 35;
                                        });
        //终止操作:一次性执行全部内容,即“惰性求值”
        stream.forEach(System.out::println);
    }

    //外部迭代
    @Test
    public void test2() {
     
        Iterator<Employee> it = employees.iterator();

        while (it.hasNext()) {
     
            System.out.println(it.next());
        }
    }

    @Test
    public void test3() {
     
        employees.stream()
                .filter((e) -> {
     
                    System.out.println("短路");
                    return e.getSalary() > 5000;
                })
                .limit(2)
                .forEach(System.out::println);
    }

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

    /**
     映射
     map——接收 Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
     flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
     */
    @Test
    public void test5() {
     
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");

        list.stream()
                .map((str) -> str.toUpperCase())
                .forEach(System.out::println);

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

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

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

        Stream<Stream<Character>> stream = list.stream()
                .map(TestStreamAPI2::filterCharacter);

        stream.forEach(sm -> {
     
            sm.forEach(System.out::println);
        });

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

        Stream<Character> sm = list.stream()
                .flatMap(TestStreamAPI2::filterCharacter);
        sm.forEach(System.out::println);
    }

    public static Stream<Character> filterCharacter(String str) {
     
        ArrayList<Character> list = new ArrayList<>();

        for (Character c: str.toCharArray()) {
     
            list.add(c);
        }

        return list.stream();
    }

    @Test
    public void test6() {
     
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");

        List list2 = new ArrayList();
        list2.add(11);
        list2.add(22);
        list2.add(list);

        System.out.println(list2);

        List list3 = new ArrayList();
        list3.add(11);
        list3.add(22);
        list3.addAll(list);

        System.out.println(list3);
    }

    /**
     排序
     sorted()——自然排序(Comparable)
     sorted(Comparator com)——定制排序(Comparator)
     */
    @Test
    public void test7() {
     
        List<String> list = Arrays.asList("ccc", "aaa", "bbb", "ddd", "eee");

        list.stream()
                .sorted()
                .forEach(System.out::println);

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

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

    }
}

11. Java8新特性_Stream_查找与匹配

Stream 的终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。

  • 查找与匹配
方 法 描 述
allMatch(Predicate p) 检查是否匹配所有元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代
相反,Stream API 使用内部迭代——它帮你把迭代做了)

12. Java8新特性_Stream_归约与收集

  • 归约
方 法 描 述
reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。
返回 T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。
返回 Optional

备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。

  • 收集
方 法 描 述
collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

方法 返回类型 作用
toList List 把流中元素收集到List
List emps = list.stream().collect(Collectors.toList());
toSet Set 把流中元素收集到Set
Set emps = list.stream().collect(Collectors.toSet());
toCollection Collection 把流中元素收集到创建的集合
Collection emps = list.stream().collect(Collectors.toCollection(ArrayList::new));
counting Long 计算流中元素的个数
long count = list.stream().collect(Collectors.counting());
summingInt Integer 对流中元素的整数属性求和
int total = list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingInt Double 计算流中元素Integer属性的平均值
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
summarizingInt IntSummaryStatistics 收集流中Integer属性的统计值。
如:平均值
IntSummaryStatistics iss = list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
joining String 连接流中每个字符串
String str = list.stream().map(Employee::getName).collect(Collectors.joining());
maxBy Optional 根据比较器选择最大值
Optional max = list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minBy Optional 根据比较器选择最小值
Optional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
reducing 归约产生的类型 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
int total = list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果转换函数
int how = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingBy Map> 根据某属性值对流分组,属性为K,结果为V
Map> map = list.stream().collect(Collectors.groupingBy(Employee::getStatus));
partitioningBy Map> 根据true或false进行分区
Map> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));
package day02.com.lm.java8;

import org.junit.Test;

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

import day02.com.lm.java8.Employee.Status;

public class TestStreamAPI3 {
     

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99, Status.FREE),
            new Employee("李四", 58, 5555.55, Status.BUSY),
            new Employee("王五", 26, 3333.33, Status.VOCATION),
            new Employee("赵六", 36, 6666.66, Status.FREE),
            new Employee("田七", 12, 8888.88, Status.BUSY),
            new Employee("田七", 12, 8888.88, Status.BUSY)
    );

    /*
        查找与匹配
        allMatch——检查是否匹配所有元素
        anyMatch——检查是否至少匹配一个元素
        noneMatch——检查是否没有匹配所有元素
        findFirst——返回第一个元素
        findAny——返回当前流中的任意元素
        count——返回流中元素的总个数
        max——返回流中最大值
        min——返回流中最小值
     */
    @Test
    public void test1() {
     
        boolean b1 = employees.stream()
                .allMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(b1);

        boolean b2 = employees.stream()
                .anyMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(b2);

        boolean b3 = employees.stream()
                .noneMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(b3);

        Optional<Employee> op = employees.stream()
                .sorted((e1, e2) -> -Double.compare(e1.getSalary(), e2.getSalary()))
                .findFirst();
        System.out.println(op.get());

        Optional<Employee> op2 = employees.stream()
                .filter((e) -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(op.get());

        Optional<Employee> op3 = employees.parallelStream()
                .filter((e) -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(op3.get());
    }

    @Test
    public void test2() {
     
        Long count = employees.stream()
                .count();
        System.out.println(count);

        Optional<Employee> op1 = employees.stream()
                .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(op1.get());

        Optional<Double> op2 = employees.stream()
                .map(Employee::getSalary)
                .min(Double::compareTo);
        System.out.println(op2.get());
    }

    /*
        规约
        reduce(T identity, BinaryOperator) / reduce(BinaryOperator)——可以将流中元素反复结合起来,得到一个值
     */
    @Test
    public void test3() {
     
        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);
        System.out.println(sum);

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

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

    /*
        收集
        collect——将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
     */
    @Test
    public void test4() {
     
        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);
    }

    @Test
    public void test5() {
     
        //总数
        Long count = employees.stream()
                .collect(Collectors.counting());
        System.out.println(count);

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

        //平均值
        Double avg = employees.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);

        //总和
        Double sum = employees.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);

        //最大值
        Optional<Employee> max = employees.stream()
                .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(max.get());

        //最小值
        Optional<Double> min = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));
        System.out.println(min.get());
    }

    //分组
    @Test
    public void test6() {
     
        Map<Status, List<Employee>> map = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);
    }

    //多级分组
    @Test
    public void test7() {
     
        Map<Status, Map<String, List<Employee>>> map = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
     
                    if (((Employee) e).getAge() <= 35) {
     
                        return "青年";
                    } else if (((Employee) e).getAge() <= 50) {
     
                        return "中年";
                    } else {
     
                        return "老年";
                    }
                })));
        System.out.println(map);
    }

    //分片
    @Test
    public void test8() {
     
        Map<Boolean, List<Employee>> map = employees.stream()
                .collect(Collectors.partitioningBy((e) -> e.getSalary() > 8000.00));
        System.out.println(map);
    }

    @Test
    public void test9() {
     
        DoubleSummaryStatistics dss = employees.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(dss.getSum());
        System.out.println(dss.getAverage());
        System.out.println(dss.getMax());
    }

    @Test
    public void test10() {
     
        String str = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(",", "【", "】"));
        System.out.println(str);
    }
}

13. Java8新特性_Stream API 练习

package day02.com.lm.exer;

import day01.com.lm.java8.Employee;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class TestStreamAPI {
     

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99, Employee.Status.FREE),
            new Employee("李四", 58, 5555.55, Employee.Status.BUSY),
            new Employee("王五", 26, 3333.33, Employee.Status.VOCATION),
            new Employee("赵六", 36, 6666.66, Employee.Status.FREE),
            new Employee("田七", 12, 8888.88, Employee.Status.BUSY),
            new Employee("田七", 12, 8888.88, Employee.Status.BUSY)
    );

    /*
        1.给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?
        ,给定【1,2,3,4,5】,应该返回【1,4,9,16,25】。
     */
    @Test
    public void test1() {
     
        Integer[] nums = new Integer[]{
     1, 2, 3, 4, 5};
        Arrays.stream(nums)
                .map((x) -> x * x)
                .forEach(System.out::println);
    }

    /*
        2.怎样用map和 reduce方法数一数流中有多少个Emp1oyee呢?
     */
    @Test
    public void test2() {
     
        Optional<Integer> op = employees.stream()
                .map((e) -> 1)
                .reduce(Integer::sum);
        System.out.println(op.get());
    }
}
package day02.com.lm.exer;

/**
 * 交易员类
 */
public class Trader {
     
    private String name;
    private String city;

    public Trader() {
     
    }

    public Trader(String name, String city) {
     
        this.name = name;
        this.city = city;
    }

    public String getName() {
     
        return name;
    }

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

    public String getCity() {
     
        return city;
    }

    public void setCity(String city) {
     
        this.city = city;
    }

    @Override
    public String toString() {
     
        return "Trader{" +
                "name='" + name + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}
package day02.com.lm.exer;

/**
 * 交易类
 */
public class Transaction {
     
    private Trader trader;
    private int year;
    private int value;

    public Transaction() {
     
    }

    public Transaction(Trader trader, int year, int value) {
     
        this.trader = trader;
        this.year = year;
        this.value = value;
    }

    public Trader getTrader() {
     
        return trader;
    }

    public void setTrader(Trader trader) {
     
        this.trader = trader;
    }

    public int getYear() {
     
        return year;
    }

    public void setYear(int year) {
     
        this.year = year;
    }

    public int getValue() {
     
        return value;
    }

    public void setValue(int value) {
     
        this.value = value;
    }

    @Override
    public String toString() {
     
        return "Transaction{" +
                "trader=" + trader +
                ", year=" + year +
                ", value=" + value +
                '}';
    }
}
package day02.com.lm.exer;

import org.junit.Before;
import org.junit.Test;

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

public class TestTransaction {
     
    List<Transaction> transactions = null;

    @Before
    public void before() {
     
        Trader raoul = new Trader( "Raoul","Cambridge");
        Trader mario = new Trader("Mario","Milan");
        Trader alan = new Trader ("Alan","Cambridge");
        Trader brian = new Trader("Brian","Cambridge");

        transactions = Arrays.asList(
                new Transaction(brian, 2011, 300),
                new Transaction(raoul, 2012, 1000),
                new Transaction (raoul, 2011, 400),
                new Transaction (mario, 2012, 710),
                new Transaction(mario, 2012, 700),
                new Transaction(alan, 2012, 950)
        );
    }

    // 1.找出2011年发生的所有交易,并按交易额排序(从低到高)
    @Test
    public void test1() {
     
        transactions.stream()
                .filter((t) -> t.getYear() == 2011)
                .sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
                .forEach(System.out::println);
    }
    // 2.交易员都在哪些不同的城市工作过?
    @Test
    public void test2() {
     
        transactions.stream()
                .map((t) -> t.getTrader().getCity())
                .distinct()
                .forEach(System.out::println);
    }
    // 3.查找所有来自剑桥的交易员,并按姓名排序。
    @Test
    public void test3() {
     
        transactions.stream()
                .filter((t) -> t.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getTrader)
                .distinct()
                .sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
                .forEach(System.out::println);
    }
    // 4.返回所有交易员的姓名字符串,按字母顺序排序
    @Test
    public void test4() {
     
        transactions.stream()
                .map((t) -> t.getTrader().getName())
                .distinct()
                .sorted()
                .forEach(System.out::println);

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

        String str = transactions.stream()
                .map((t) -> t.getTrader().getName())
                .distinct()
                .sorted()
                .reduce("", String::concat);
        System.out.println(str);

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

        transactions.stream()
                .map((t) -> t.getTrader().getName())
                .distinct()
                .flatMap(this::filterCharater)
                .sorted((s1, s2) -> s1.compareToIgnoreCase(s2))
                .forEach(System.out::print);
    }

    private Stream<String> filterCharater(String str) {
     
        List<String> list = new ArrayList<>();
        for (Character c : str.toCharArray()) {
     
            list.add(c.toString());
        }
        return list.stream();
    }
    // 5.有没有交易员是在米兰工作的?
    @Test
    public void test5() {
     
        boolean bl = transactions.stream()
                .anyMatch((t) -> t.getTrader().getCity().equals("Milan"));
        System.out.println(bl);
    }
    // 6.打印生活在剑桥的交易员的所有交易额
    @Test
    public void test6() {
     
        Optional<Integer> sum = transactions.stream()
                .filter((t) -> t.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getValue)
                .reduce(Integer::sum);
        System.out.println(sum.get());
    }
    // 7.所有交易中,最高的交易额是多少
    @Test
    public void test7() {
     
        Optional<Integer> max = transactions.stream()
                .map((t) -> t.getValue())
                .max(Integer::compare);
        System.out.println(max.get());
    }
    // 8.找到交易额最小的交易
    @Test
    public void test8() {
     
        Optional<Transaction> min = transactions.stream()
                .min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()));
        System.out.println(min.get());
    }
}

14. Java8新特性_并行流与串行流

传统线程存在的问题:某些线程可能阻塞繁忙,某些线程可能顺畅清闲,从而导致线程使用效率低下。
Java8新特性_第13张图片

并行流与串行流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。

Fork/Join 框架

Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。
Java8新特性_第14张图片
Fork/Join 框架与传统线程池的区别
采用 “工作窃取”模式(work-stealing):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上。在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态。
而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行。这种方式减少了线程的等待时间,提高了性能。

package day02.com.lm.java8;

import java.util.concurrent.RecursiveTask;

public class ForkJoinCalculate extends RecursiveTask<Long> {
     

    private long start;
    private long end;

    private static final long THRESHOLD = 10000;

    public ForkJoinCalculate(long start, long end) {
     
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
     
        long length = end - start;

        if (length <= THRESHOLD) {
     
            long sum = 0;

            for (long i = start; i <= end; i++) {
     
                sum += i;
            }

            return sum;
        } else {
     
            long middle = (start + end) / 2;
            ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
            left.fork();// 拆分子任务,同时压入线程队列

            ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
            right.fork();

            return left.join() + right.join();
        }
    }
}
package day02.com.lm.java8;

import org.junit.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class TestForkJoin {
     

    /**
     * ForkJoin 框架
     */
    @Test
    public void test1() {
     
        Instant start = Instant.now();

        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinCalculate(0, 100000000000L);
        Long sum = pool.invoke(task);
        System.out.println(sum);

        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start, end).toMillis());//15999
    }

    /**
     * 普通 for
     */
    @Test
    public void test2() {
     
        Instant start = Instant.now();
        long sum = 0L;

        for (long i = 0; i <= 100000000000L; i++) {
     
            sum += i;
        }

        System.out.println(sum);

        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start, end).toMillis());//28264
    }

    /**
     * java8 并行流
     */
    @Test
    public void test3() {
     
        Instant start = Instant.now();
        LongStream.rangeClosed(0, 100000000000L)
                .parallel()
                .reduce(0, Long::sum);

        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start, end).toMillis());//11216
    }
}

15. Java8新特性_Optional 容器类

Optional 容器类的常用方法:

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

import org.junit.Test;

import java.util.Optional;

public class TestOptional {
     
    /*
        Optional 容器类的常用方法:
        Optional.of(T t):创建一个 Optiona1实例
        Optional.empty():创建一个空的 Optiona1实例
        Optional.ofNu1lable(T t):若t不为nu11,创建 Optional实例,否则创建空实例
        isPresent():判断是否包含值
        orElse(T t):如果调用对象包含值,返回该值,否则返回t
        orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值
        map(Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()
        f1atMap(Function mapper):与map类似,要求返回值必须是 Optional
     */
    @Test
    public void test1() {
     
        Optional<Employee> op = Optional.of(null);  //java.lang.NullPointerException
        Employee emp = op.get();
        System.out.println(emp);
    }

    @Test
    public void test2() {
     
        Optional<Object> op = Optional.empty();
        System.out.println(op.get());   //java.util.NoSuchElementException: No value present
    }

    @Test
    public void test3() {
     
        Optional<Employee> op = Optional.ofNullable(null);
        System.out.println(op.get()); //java.util.NoSuchElementException: No value present

        Optional<Object> op1 = Optional.ofNullable(null);
        if (op1.isPresent()) {
         //false
            System.out.println(op1.get());
        }

        Optional<Employee> op2 = Optional.ofNullable(new Employee());
        if (op2.isPresent()) {
         //true
            System.out.println(op2.get());
        }

        Optional<Employee> op3 = Optional.ofNullable(new Employee());
        Employee emp3 = op3.orElse(new Employee("张三", 22, 8888));
        System.out.println(emp3);    //Employee{id=0, name='null', age=null, salary=0.0, status=null}

        Optional<Employee> op4 = Optional.ofNullable(null);
        Employee emp4 = op4.orElse(new Employee("张三", 22, 8888));
        System.out.println(emp4);   //Employee{id=0, name='张三', age=22, salary=8888.0, status=null}

        Optional<Employee> op5 = Optional.ofNullable(null);
        Employee emp5 = op5.orElseGet(() -> new Employee());
        System.out.println(emp5);   //Employee{id=0, name='null', age=null, salary=0.0, status=null}
    }

    @Test
    public void test4() {
     
        Optional<Employee> op = Optional.ofNullable(new Employee("张三", 22, 8888));
        Optional<String> opStr = op.map((e) -> e.getName());
        System.out.println(opStr.get());    //张三

        Optional<String> opStr2 = op.flatMap((e) -> Optional.of(e.getName()));
        System.out.println(opStr2.get());   //张三
    }

    // 需求:获取一个男人心中女神的名字
    public String getGodnessName(Man man) {
     
        if (man != null) {
     
            Godness gn = man.getGodness();

            if (gn != null) {
     
                return gn.getName();
            }
        }
        return "小红";
    }

    // 例题
    @Test
    public void test5() {
     
        Man man = new Man();

        String n = getGodnessName(man);
        System.out.println(n);//小红

        Optional<NewMan> op = Optional.ofNullable(null);
        String str = getGodnessName2(op);
        System.out.println(str);//小花

        op = Optional.ofNullable(new NewMan());
        str = getGodnessName2(op);
        System.out.println(str);//小花

        Optional<Godness> gn = Optional.ofNullable(new Godness("小芳"));
        op = Optional.ofNullable(new NewMan(gn));
        str = getGodnessName2(op);
        System.out.println(str);//小芳
    }

    public String getGodnessName2(Optional<NewMan> man) {
     
        return man.orElse(new NewMan())
                .getGodness().orElse(new Godness("小花"))
                .getName();
    }
}
package day02.com.lm.java8;

public class Man {
     
    private Godness godness;

    public Man() {
     
    }

    public Man(Godness godness) {
     
        this.godness = godness;
    }

    public Godness getGodness() {
     
        return godness;
    }

    public void setGodness(Godness godness) {
     
        this.godness = godness;
    }

    @Override
    public String toString() {
     
        return "Man{" +
                "godness=" + godness +
                '}';
    }
}
package day02.com.lm.java8;

public class Godness {
     

    private String name;

    public Godness() {
     
    }

    public Godness(String name) {
     
        this.name = name;
    }

    public String getName() {
     
        return name;
    }

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

    @Override
    public String toString() {
     
        return "Godness{" +
                "name='" + name + '\'' +
                '}';
    }
}
package day02.com.lm.java8;

import java.util.Optional;

public class NewMan {
     

    private Optional<Godness> godness = Optional.empty();

    public NewMan() {
     
    }

    public NewMan(Optional<Godness> godness) {
     
        this.godness = godness;
    }

    public Optional<Godness> getGodness() {
     
        return godness;
    }

    public void setGodness(Optional<Godness> godness) {
     
        this.godness = godness;
    }

    @Override
    public String toString() {
     
        return "NewMan{" +
                "godness=" + godness +
                '}';
    }
}

16. Java8新特性_接口中的默认方法与静态方法

6-接口中的默认方法与静态方法

接口中的默认方法
Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用 default 关键字修饰。
例如:
Java8新特性_第15张图片
接口默认方法的”类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时

  • 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
  • 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。
    Java8新特性_第16张图片
    接口中的静态方法
    Java8 中,接口中允许添加静态方法。
    例如:
    Java8新特性_第17张图片
package day02.com.lm.java8;

public class MyClass {
     

    public String getName() {
     
        return "类嘿嘿嘿";
    }

}
package day02.com.lm.java8;

public interface MyFun {
     

    default String getName() {
     
        return "接口哈哈哈";
    }

}
package day02.com.lm.java8;

public interface MyInterface {
     

    default String getName() {
     
        return "接口2呵呵呵";
    }

    public static void show() {
     
        System.out.println("接口中的静态方法");
    }

}
package day02.com.lm.java8;

public class SubClass extends MyClass implements MyFun {
     

}
package day02.com.lm.java8;

public class SubClass2 implements MyFun, MyInterface {
     

    @Override
    public String getName() {
     
//        return MyInterface.super.getName();
        return MyFun.super.getName();
    }

}
package day02.com.lm.java8;

public class TestDefaultInterface {
     

    public static void main(String[] args) {
     
        SubClass sc = new SubClass();
        System.out.println(sc.getName());//类嘿嘿嘿

        SubClass2 sc2 = new SubClass2();//接口哈哈哈
        System.out.println(sc2.getName());

        MyInterface.show();//接口中的静态方法
    }
}

17. Java8新特性_传统时间格式化的线程安全问题

5-新时间日期 API

使用 LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法

方法 描述 示例
now() 静态方法,根据当前时间创建对象 LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
of() 静态方法,根据指定日期/时间创建对象 LocalDate localDate = LocalDate.of(2016, 10, 26);
LocalTime localTime = LocalTime.of(02, 22, 56);
LocalDateTime localDateTime = LocalDateTime.of(2016, 10, 26, 12, 10, 55);
plusDays, plusWeeks, plusMonths, plusYears 向当前 LocalDate 对象添加几天、几周、几个月、几年
minusDays, minusWeeks, minusMonths, minusYears 从当前 LocalDate 对象减去几天、几周、几个月、几年
plus, minus 添加或减少一个 Duration 或 Period
withDayOfMonth, withDayOfYear, withMonth, withYear 将月份天数、年份天数、月份、年份修改为指定的值并返回新的LocalDate 对象
getDayOfMonth 获得月份天数(1-31)
getDayOfYear 获得年份天数(1-366)
getDayOfWeek 获得星期几(返回一个 DayOfWeek 枚举值)
getMonth 获得月份, 返回一个 Month 枚举值
getMonthValue 获得月份(1-12)
getYear 获得年份
until 获得两个日期之间的 Period 对象,或者指定 ChronoUnits 的数字
isBefore, isAfter 比较两个 LocalDate
isLeapYear 判断是否是闰年

Instant 时间戳
用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算

Duration 和 Period

  • Duration:用于计算两个“时间”间隔
  • Period:用于计算两个“日期”间隔

日期的操纵

  • TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
  • TemporalAdjusters : 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。
    例如获取下个周日:
    Java8新特性_第18张图片

解析与格式化
java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:

  • 预定义的标准格式
  • 语言环境相关的格式
  • 自定义的格式

时区的处理
Java8 中加入了对时区的支持,带时区的时间为分别为:
ZonedDate、ZonedTime、ZonedDateTime
其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式
例如 :Asia/Shanghai 等
ZoneId:该类中包含了所有的时区信息
  getAvailableZoneIds() : 可以获取所有时区信息
  of(id) : 用指定的时区信息获取 ZoneId 对象

与传统日期处理的转换

To 遗留类 From 遗留类
java.time.Instant
java.util.Date
Date.from(instant) date.toInstant()
java.time.Instant
java.sql.Timestamp
Timestamp.from(instant) timestamp.toInstant()
java.time.ZonedDateTime
java.util.GregorianCalendar
GregorianCalendar.from(zonedDateTime) cal.toZonedDateTime()
java.time.LocalDate
java.sql.Time
Date.valueOf(localDate) date.toLocalDate()
java.time.LocalTime
java.sql.Time
Date.valueOf(localDate) date.toLocalTime()
java.time.LocalDateTime
java.sql.Timestamp
Timestamp.valueOf(localDateTime) timestamp.toLocalDateTime()
java.time.ZoneId
java.util.TimeZone
Timezone.getTimeZone(id) timeZone.toZoneId()
java.time.format.DateTimeFormatter
java.text.DateFormat
formatter.toFormat()
package day03.com.lm.java8;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestSimpleDateFormat {
     
    public static void main(String[] args) throws Exception {
     
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

        Callable<Date> task = new Callable<Date>() {
     
            @Override
            public Date call() throws Exception {
     
                return sdf.parse("20161218");
            }
        };

        ExecutorService pool = Executors.newFixedThreadPool(10);

        List<Future<Date>> results = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
     
            results.add(pool.submit(task));
        }

        for (Future<Date> future : results) {
     
            System.out.println(future.get());
        }

        pool.shutdown();
    }
}

上述代码有线程安全问题,运行会报如下错:

Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: ".1122E2.1122E2"
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at day02.com.lm.java8.TestSimpleDateFormat.main(TestSimpleDateFormat.java:35)
Caused by: java.lang.NumberFormatException: For input string: ".1122E2.1122E2"
	at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
	at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
	at java.lang.Double.parseDouble(Double.java:538)
	at java.text.DigitList.getDouble(DigitList.java:169)
	at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1867)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at day02.com.lm.java8.TestSimpleDateFormat$1.call(TestSimpleDateFormat.java:21)
	at day02.com.lm.java8.TestSimpleDateFormat$1.call(TestSimpleDateFormat.java:18)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:748)

改成用ThreadLocal给DateFormat加上锁:

package day03.com.lm.java8;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatThreadLocal {
     

    private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
     
        @Override
        protected DateFormat initialValue() {
     
            return new SimpleDateFormat("yyyyMMdd");
        }
    };

    public static Date convert(String source) throws ParseException {
     
        return df.get().parse(source);
    }
}
package day02.com.lm.java8;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestSimpleDateFormat {
     
    public static void main(String[] args) throws Exception {
     
        Callable<Date> task = new Callable<Date>() {
     
            @Override
            public Date call() throws Exception {
     
                return DateFormatThreadLocal.convert("20161218");
            }
        };

        ExecutorService pool = Executors.newFixedThreadPool(10);

        List<Future<Date>> results = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
     
            results.add(pool.submit(task));
        }

        for (Future<Date> future : results) {
     
            System.out.println(future.get());
        }

        pool.shutdown();
    }
}

运行结果正常:

Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016

Java8,全新的日期时间API:

package day03.com.lm.java8;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestSimpleDateFormat {
     
    public static void main(String[] args) throws Exception {
     

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");

        Callable<LocalDate> task = new Callable<LocalDate>() {
     
            @Override
            public LocalDate call() throws Exception {
     
                return LocalDate.parse("20161218", dtf);
            }
        };

        ExecutorService pool = Executors.newFixedThreadPool(10);

        List<Future<LocalDate>> results = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
     
            results.add(pool.submit(task));
        }

        for (Future<LocalDate> future : results) {
     
            System.out.println(future.get());
        }

        pool.shutdown();
    }
}

运行结果如下:

2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18

18. Java8新特性_新时间与日期 API-本地时间与时间戳

package day03.com.lm.java8;

import org.junit.Test;

import java.time.*;

public class TestLocalDateTime {
     
    //1. LocalDate   LocalTime   LocalDateTime
    @Test
    public void test1() {
     
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);    //  2021-05-04T15:20:47.034

        LocalDateTime ldt2 = LocalDateTime.of(2015, 10, 19, 13, 22, 33);
        System.out.println(ldt2);    //  2015-10-19T13:22:33

        LocalDateTime ldt3 = ldt.plusYears(2);
        System.out.println(ldt3);    //  2023-05-04T15:20:47.034

        LocalDateTime ldt4 = ldt.minusMonths(2);
        System.out.println(ldt4);    //  2021-03-04T15:20:47.034

        System.out.println(ldt.getYear());    //  2021
        System.out.println(ldt.getMonthValue());    //  5
        System.out.println(ldt.getDayOfMonth());    //  4
        System.out.println(ldt.getHour());    //  15
        System.out.println(ldt.getMinute());    //  20
        System.out.println(ldt.getSecond());    //  47
    }

    //2. Instant : 时间戳(以 Unix 元年:1970年1月1日 00:00:00 到某个时间之间的毫秒值)
    @Test
    public void test2() {
     
        Instant ins1 = Instant.now();// 默认获取 UTC 时区
        System.out.println(ins1);   //  2021-05-04T07:19:47.759Z

        OffsetDateTime odt = ins1.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);    //  2021-05-04T15:19:47.759+08:00

        System.out.println(ins1.toEpochMilli());    //  1620112787759

        Instant ins2 = Instant.ofEpochSecond(1);
        System.out.println(ins2);   //  1970-01-01T00:00:01Z

        OffsetDateTime odt2 = ins2.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt2);   //  1970-01-01T08:00:01+08:00
    }

    //3.
    //Duration : 计算两个“时间”之间的间隔
    //Period : 计算两个“日期”之间的间隔
    @Test
    public void test3() {
     
        Instant ins1 = Instant.now();

        try {
     
            Thread.sleep(1000);
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }

        Instant ins2 = Instant.now();

        Duration duration = Duration.between(ins1, ins2);
        System.out.println(duration);   //  PT1.01S
        System.out.println(duration.toMillis());    //  1010

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

        LocalTime lt1 = LocalTime.now();

        try {
     
            Thread.sleep(1000);
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }

        LocalTime lt2 = LocalTime.now();

        Duration duration2 = Duration.between(lt1, lt2);
        System.out.println(duration2);  //  PT1.001S
        System.out.println(duration2.toMillis());   //  1001
    }

    @Test
    public void test4() {
     
        LocalDate ld1 = LocalDate.of(2020,1,1);
        LocalDate ld2 = LocalDate.now();
        Period period = Period.between(ld1, ld2);
        System.out.println(period); //  P1Y4M3D
        System.out.println(period.getYears());  //  1
        System.out.println(period.getMonths()); //  4
        System.out.println(period.getDays());   //  3
    }
}

19. Java8新特性_新时间和日期 API-时间校正器

package day03.com.lm.java8;

import org.junit.Test;

import java.time.*;
import java.time.temporal.TemporalAdjusters;

public class TestNewDateAPI {
     

    //TemporalAdjuster : 时间校正器
    @Test
    public void test5() {
     
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);    //  2021-05-04T16:03:14.880

        LocalDateTime ldt2 = ldt.withDayOfMonth(10);
        System.out.println(ldt2);    //  2021-05-10T16:03:14.880

        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(ldt3);    //  2021-05-09T16:03:14.880

        //自定义:下一个工作日
        LocalDateTime nextWorkDay = ldt.with((l) -> {
     
            LocalDateTime ldt4 = (LocalDateTime) l;

            DayOfWeek dow = ldt4.getDayOfWeek();

            if (dow.equals(DayOfWeek.FRIDAY)) {
     
                return ldt4.plusDays(3);
            } else if (dow.equals(DayOfWeek.SATURDAY)) {
     
                return ldt4.plusDays(2);
            } else {
     
                return ldt4.plusDays(1);
            }
        });

        System.out.println(nextWorkDay);    //  2021-05-05T16:03:14.880
    }
}

20. Java8新特性_新时间和日期 API-时间格式化与时区的处理

package day03.com.lm.java8;

import org.junit.Test;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Set;

public class TestNewDateAPI {
     
    //ZonedDate、ZonedTime、ZonedDateTime
    @Test
    public void test7() {
     
        Set<String> set = ZoneId.getAvailableZoneIds();
        set.forEach(str -> System.out.print(str+"  "));
        //  Asia/Aden  America/Cuiaba  Etc/GMT+9  Etc/GMT+8  Africa/Nairobi  America/Marigot  Asia/Aqtau  
        //  Pacific/Kwajalein  America/El_Salvador  Asia/Pontianak  Africa/Cairo  Pacific/Pago_Pago  Africa/Mbabane  
        //  Asia/Kuching  Pacific/Honolulu  Pacific/Rarotonga  America/Guatemala  Australia/Hobart  Europe/London  
        //  America/Belize  America/Panama  Asia/Chungking  America/Managua  America/Indiana/Petersburg  Asia/Yerevan
        //  Europe/Brussels  GMT  Europe/Warsaw  America/Chicago  Asia/Kashgar  Chile/Continental  Pacific/Yap  CET  
        //  Etc/GMT-1  Etc/GMT-0  Europe/Jersey  America/Tegucigalpa  Etc/GMT-5  Europe/Istanbul  America/Eirunepe  
        //  Etc/GMT-4  America/Miquelon  Etc/GMT-3  Europe/Luxembourg  Etc/GMT-2  Etc/GMT-9  
        //  America/Argentina/Catamarca  Etc/GMT-8  Etc/GMT-7  Etc/GMT-6  Europe/Zaporozhye  Canada/Yukon  
        //  Canada/Atlantic  Atlantic/St_Helena  Australia/Tasmania  Libya  Europe/Guernsey  America/Grand_Turk  
        //  US/Pacific-New  Asia/Samarkand  America/Argentina/Cordoba  Asia/Phnom_Penh  Africa/Kigali  Asia/Almaty  
        //  US/Alaska  Asia/Dubai  Europe/Isle_of_Man  America/Araguaina  Cuba  Asia/Novosibirsk  
        //  America/Argentina/Salta  Etc/GMT+3  Africa/Tunis  Etc/GMT+2  Etc/GMT+1  Pacific/Fakaofo  Africa/Tripoli  
        //  Etc/GMT+0  Israel  Africa/Banjul  Etc/GMT+7  Indian/Comoro  Etc/GMT+6  Etc/GMT+5  Etc/GMT+4  
        //  Pacific/Port_Moresby  US/Arizona  Antarctica/Syowa  Indian/Reunion  Pacific/Palau  Europe/Kaliningrad  
        //  America/Montevideo  Africa/Windhoek  Asia/Karachi  Africa/Mogadishu  Australia/Perth  Brazil/East  
        //  Etc/GMT  Asia/Chita  Pacific/Easter  Antarctica/Davis  Antarctica/McMurdo  Asia/Macao  America/Manaus  
        //  Africa/Freetown  Europe/Bucharest  Asia/Tomsk  America/Argentina/Mendoza  Asia/Macau  Europe/Malta  
        //  Mexico/BajaSur  Pacific/Tahiti  Africa/Asmera  Europe/Busingen  America/Argentina/Rio_Gallegos  
        //  Africa/Malabo  Europe/Skopje  America/Catamarca  America/Godthab  Europe/Sarajevo  Australia/ACT  GB-Eire
        //  Africa/Lagos  America/Cordoba  Europe/Rome  Asia/Dacca  Indian/Mauritius  Pacific/Samoa  America/Regina  
        //  America/Fort_Wayne  America/Dawson_Creek  Africa/Algiers  Europe/Mariehamn  America/St_Johns  
        //  America/St_Thomas  Europe/Zurich  America/Anguilla  Asia/Dili  America/Denver  Africa/Bamako  
        //  Europe/Saratov  GB  Mexico/General  Pacific/Wallis  Europe/Gibraltar  Africa/Conakry  Africa/Lubumbashi  
        //  Asia/Istanbul  America/Havana  NZ-CHAT  Asia/Choibalsan  America/Porto_Acre  Asia/Omsk  Europe/Vaduz  
        //  US/Michigan  Asia/Dhaka  America/Barbados  Europe/Tiraspol  Atlantic/Cape_Verde  Asia/Yekaterinburg  
        //  America/Louisville  Pacific/Johnston  Pacific/Chatham  Europe/Ljubljana  America/Sao_Paulo  Asia/Jayapura
        //  America/Curacao  Asia/Dushanbe  America/Guyana  America/Guayaquil  America/Martinique  Portugal  
        //  Europe/Berlin  Europe/Moscow  Europe/Chisinau  America/Puerto_Rico  America/Rankin_Inlet  Pacific/Ponape 
        //  Europe/Stockholm  Europe/Budapest  America/Argentina/Jujuy  Australia/Eucla  Asia/Shanghai  Universal  
        //  Europe/Zagreb  America/Port_of_Spain  Europe/Helsinki  Asia/Beirut  Asia/Tel_Aviv  Pacific/Bougainville  
        //  US/Central  Africa/Sao_Tome  Indian/Chagos  America/Cayenne  Asia/Yakutsk  Pacific/Galapagos  
        //  Australia/North  Europe/Paris  Africa/Ndjamena  Pacific/Fiji  America/Rainy_River  Indian/Maldives  
        //  Australia/Yancowinna  SystemV/AST4  Asia/Oral  America/Yellowknife  Pacific/Enderbury  America/Juneau  
        //  Australia/Victoria  America/Indiana/Vevay  Asia/Tashkent  Asia/Jakarta  Africa/Ceuta  Asia/Barnaul  
        //  America/Recife  America/Buenos_Aires  America/Noronha  America/Swift_Current  Australia/Adelaide  
        //  America/Metlakatla  Africa/Djibouti  America/Paramaribo  Europe/Simferopol  Europe/Sofia  
        //  Africa/Nouakchott  Europe/Prague  America/Indiana/Vincennes  Antarctica/Mawson  America/Kralendijk  
        //  Antarctica/Troll  Europe/Samara  Indian/Christmas  America/Antigua  Pacific/Gambier  America/Indianapolis
        //  America/Inuvik  America/Iqaluit  Pacific/Funafuti  UTC  Antarctica/Macquarie  Canada/Pacific  
        //  America/Moncton  Africa/Gaborone  Pacific/Chuuk  Asia/Pyongyang  America/St_Vincent  Asia/Gaza  
        //  Etc/Universal  PST8PDT  Atlantic/Faeroe  Asia/Qyzylorda  Canada/Newfoundland  America/Kentucky/Louisville
        //  America/Yakutat  Asia/Ho_Chi_Minh  Antarctica/Casey  Europe/Copenhagen  Africa/Asmara  Atlantic/Azores  
        //  Europe/Vienna  ROK  Pacific/Pitcairn  America/Mazatlan  Australia/Queensland  Pacific/Nauru  
        //  Europe/Tirane  Asia/Kolkata  SystemV/MST7  Australia/Canberra  MET  Australia/Broken_Hill  Europe/Riga  
        //  America/Dominica  Africa/Abidjan  America/Mendoza  America/Santarem  Kwajalein  America/Asuncion  
        //  Asia/Ulan_Bator  NZ  America/Boise  Australia/Currie  EST5EDT  Pacific/Guam  Pacific/Wake  
        //  Atlantic/Bermuda  America/Costa_Rica  America/Dawson  Asia/Chongqing  Eire  Europe/Amsterdam  
        //  America/Indiana/Knox  America/North_Dakota/Beulah  Africa/Accra  Atlantic/Faroe  Mexico/BajaNorte  
        //  America/Maceio  Etc/UCT  Pacific/Apia  GMT0  America/Atka  Pacific/Niue  Canada/East-Saskatchewan  
        //  Australia/Lord_Howe  Europe/Dublin  Pacific/Truk  MST7MDT  America/Monterrey  America/Nassau  
        //  America/Jamaica  Asia/Bishkek  America/Atikokan  Atlantic/Stanley  Australia/NSW  US/Hawaii  SystemV/CST6
        //  Indian/Mahe  Asia/Aqtobe  America/Sitka  Asia/Vladivostok  Africa/Libreville  Africa/Maputo  Zulu  
        //  America/Kentucky/Monticello  Africa/El_Aaiun  Africa/Ouagadougou  America/Coral_Harbour  
        //  Pacific/Marquesas  Brazil/West  America/Aruba  America/North_Dakota/Center  America/Cayman  
        //  Asia/Ulaanbaatar  Asia/Baghdad  Europe/San_Marino  America/Indiana/Tell_City  America/Tijuana  
        //  Pacific/Saipan  SystemV/YST9  Africa/Douala  America/Chihuahua  America/Ojinaga  Asia/Hovd  
        //  America/Anchorage  Chile/EasterIsland  America/Halifax  Antarctica/Rothera  America/Indiana/Indianapolis 
        //  US/Mountain  Asia/Damascus  America/Argentina/San_Luis  America/Santiago  Asia/Baku  
        //  America/Argentina/Ushuaia  Atlantic/Reykjavik  Africa/Brazzaville  Africa/Porto-Novo  America/La_Paz  
        //  Antarctica/DumontDUrville  Asia/Taipei  Antarctica/South_Pole  Asia/Manila  Asia/Bangkok  
        //  Africa/Dar_es_Salaam  Poland  Atlantic/Madeira  Antarctica/Palmer  America/Thunder_Bay  
        //  Africa/Addis_Ababa  Asia/Yangon  Europe/Uzhgorod  Brazil/DeNoronha  Asia/Ashkhabad  Etc/Zulu  
        //  America/Indiana/Marengo  America/Creston  America/Punta_Arenas  America/Mexico_City  Antarctica/Vostok  
        //  Asia/Jerusalem  Europe/Andorra  US/Samoa  PRC  Asia/Vientiane  Pacific/Kiritimati  America/Matamoros  
        //  America/Blanc-Sablon  Asia/Riyadh  Iceland  Pacific/Pohnpei  Asia/Ujung_Pandang  Atlantic/South_Georgia  
        //  Europe/Lisbon  Asia/Harbin  Europe/Oslo  Asia/Novokuznetsk  CST6CDT  Atlantic/Canary  America/Knox_IN  
        //  Asia/Kuwait  SystemV/HST10  Pacific/Efate  Africa/Lome  America/Bogota  America/Menominee  America/Adak  
        //  Pacific/Norfolk  Europe/Kirov  America/Resolute  Pacific/Tarawa  Africa/Kampala  Asia/Krasnoyarsk  
        //  Greenwich  SystemV/EST5  America/Edmonton  Europe/Podgorica  Australia/South  Canada/Central  
        //  Africa/Bujumbura  America/Santo_Domingo  US/Eastern  Europe/Minsk  Pacific/Auckland  Africa/Casablanca  
        //  America/Glace_Bay  Canada/Eastern  Asia/Qatar  Europe/Kiev  Singapore  Asia/Magadan  SystemV/PST8  
        //  America/Port-au-Prince  Europe/Belfast  America/St_Barthelemy  Asia/Ashgabat  Africa/Luanda  
        //  America/Nipigon  Atlantic/Jan_Mayen  Brazil/Acre  Asia/Muscat  Asia/Bahrain  Europe/Vilnius  
        //  America/Fortaleza  Etc/GMT0  US/East-Indiana  America/Hermosillo  America/Cancun  Africa/Maseru  
        //  Pacific/Kosrae  Africa/Kinshasa  Asia/Kathmandu  Asia/Seoul  Australia/Sydney  America/Lima  
        //  Australia/LHI  America/St_Lucia  Europe/Madrid  America/Bahia_Banderas  America/Montserrat  Asia/Brunei  
        //  America/Santa_Isabel  Canada/Mountain  America/Cambridge_Bay  Asia/Colombo  Australia/West  
        //  Indian/Antananarivo  Australia/Brisbane  Indian/Mayotte  US/Indiana-Starke  Asia/Urumqi  US/Aleutian  
        //  Europe/Volgograd  America/Lower_Princes  America/Vancouver  Africa/Blantyre  America/Rio_Branco  
        //  America/Danmarkshavn  America/Detroit  America/Thule  Africa/Lusaka  Asia/Hong_Kong  Iran  
        //  America/Argentina/La_Rioja  Africa/Dakar  SystemV/CST6CDT  America/Tortola  America/Porto_Velho  
        //  Asia/Sakhalin  Etc/GMT+10  America/Scoresbysund  Asia/Kamchatka  Asia/Thimbu  Africa/Harare  Etc/GMT+12  
        //  Etc/GMT+11  Navajo  America/Nome  Europe/Tallinn  Turkey  Africa/Khartoum  Africa/Johannesburg  
        //  Africa/Bangui  Europe/Belgrade  Jamaica  Africa/Bissau  Asia/Tehran  WET  Europe/Astrakhan  Africa/Juba  
        //  America/Campo_Grande  America/Belem  Etc/Greenwich  Asia/Saigon  America/Ensenada  Pacific/Midway  
        //  America/Jujuy  Africa/Timbuktu  America/Bahia  America/Goose_Bay  America/Virgin  America/Pangnirtung  
        //  Asia/Katmandu  America/Phoenix  Africa/Niamey  America/Whitehorse  Pacific/Noumea  Asia/Tbilisi  
        //  America/Montreal  Asia/Makassar  America/Argentina/San_Juan  Hongkong  UCT  Asia/Nicosia  
        //  America/Indiana/Winamac  SystemV/MST7MDT  America/Argentina/ComodRivadavia  America/Boa_Vista  
        //  America/Grenada  Asia/Atyrau  Australia/Darwin  Asia/Khandyga  Asia/Kuala_Lumpur  Asia/Famagusta  
        //  Asia/Thimphu  Asia/Rangoon  Europe/Bratislava  Asia/Calcutta  America/Argentina/Tucuman  Asia/Kabul  
        //  Indian/Cocos  Japan  Pacific/Tongatapu  America/New_York  Etc/GMT-12  Etc/GMT-11  Etc/GMT-10  
        //  SystemV/YST9YDT  Europe/Ulyanovsk  Etc/GMT-14  Etc/GMT-13  W-SU  America/Merida  EET  America/Rosario  
        //  Canada/Saskatchewan  America/St_Kitts  Arctic/Longyearbyen  America/Fort_Nelson  America/Caracas  
        //  America/Guadeloupe  Asia/Hebron  Indian/Kerguelen  SystemV/PST8PDT  Africa/Monrovia  Asia/Ust-Nera  Egypt
        //  Asia/Srednekolymsk  America/North_Dakota/New_Salem  Asia/Anadyr  Australia/Melbourne  Asia/Irkutsk  
        //  America/Shiprock  America/Winnipeg  Europe/Vatican  Asia/Amman  Etc/UTC  SystemV/AST4ADT  Asia/Tokyo  
        //  America/Toronto  Asia/Singapore  Australia/Lindeman  America/Los_Angeles  SystemV/EST5EDT  Pacific/Majuro
        //  America/Argentina/Buenos_Aires  Europe/Nicosia  Pacific/Guadalcanal  Europe/Athens  US/Pacific  
        //  Europe/Monaco  
    }

    @Test
    public void test8() {
     
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println(ldt);  //  2021-05-08T00:27:56.188
        LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Asia/Urumqi"));
        System.out.println(ldt1);  //   2021-05-07T22:27:56.189

        LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Asia/Urumqi"));
        ZonedDateTime zdt = ldt2.atZone(ZoneId.of("Asia/Urumqi"));
        System.out.println(zdt);  //    2021-05-07T22:27:56.189+06:00[Asia/Urumqi]
    }
}

21. Java8新特性_重复注解与类型注解

重复注解与类型注解

Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。
Java8新特性_第19张图片

package day03.com.lm.java8;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

@Repeatable(MyAnnotations.class)
@Target({
     TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
     

    String value() default "minge";

}
package day03.com.lm.java8;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;

@Target({
     TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
     

    MyAnnotation[] value();

}
package day03.com.lm.java8;

import org.junit.Test;

import java.lang.reflect.Method;

/**
 * 重复注解与类型注解
 */
public class TestAnnotation {
     

    //checker framework
    private /*@NonNull*/ Object obj = null;

    @Test
    public void test1() throws Exception {
     
        Class<TestAnnotation> clazz = TestAnnotation.class;
        Method m1 = clazz.getMethod("show");

        MyAnnotation[] mas = m1.getAnnotationsByType(MyAnnotation.class);

        for (MyAnnotation myAnnotation : mas) {
     
            System.out.println(myAnnotation.value());
        }
    }

    @MyAnnotation("Hello")
    @MyAnnotation("World")
    public void show(@MyAnnotation("abc") String str) {
     

    }

}

你可能感兴趣的:(后端,Java,开发,java)