目录

1.生态

  • Lambda 表达式
  • 函数式接口
  • 方法引用 / 构造器引用
  • Stream API
  • 接口中的默认方法 / 静态方法
  • 新时间日期 API
  • 其他新特性

2.新特性

  • 速度更快
  • 代码更少
  • 强大的 Stream API
  • 便于并行
  • 最大化减少空指针异常 Optional (Kotlin ?)

3.温故而知新

  • Hashmap 底层结构/原理 老话题不再阐述 …
  • 并发hashmap …
  • Java虚拟机 …
  • Java内存模型 …

正文

1.Lambda表达式

1.1匿名函数

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

1.2匿名内部类

@Test
public void test01(){
    //匿名内部类
    Comparator<Integer> comparator = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return Integer.compare(o1,o2);
        }

        @Override
        public boolean equals(Object obj) {
            return false;
        }
    };
    //调用
    TreeSet<Integer> set = new TreeSet<>(comparator);
}

1.3 Lambda

@Test
public void test02(){
    // Lambda 表达式
    Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);

    TreeSet<Integer> set = new TreeSet<>(comparator);
}

演变过程:

  • 垃圾代码 --> 策略模式 --> 匿名内部类 --> Lambda表达式
  • 操作符:->
  • 左侧:参数列表
  • 右侧:执行代码块 / Lambda 体

口诀:

  • 写死小括号,拷贝右箭头,落地大括号
  • 左右遇一括号省
  • 左侧推断类型省

语法格式:

  • 无参数,无返回值:() -> sout
  • 例如 Runnable接口:
public class Test02 {
    int num = 10; //jdk 1.7以前 必须final修饰
    @Test
    public void test01(){
        //匿名内部类
        new Runnable() {
            @Override
            public void run() {
                //在局部类中引用同级局部变量
                //只读
                System.out.println("Hello World" + num);
            }
        };
    }

    @Test
    public void test02(){
        //语法糖
        Runnable runnable = () -> {
            System.out.println("Hello Lambda");
        };
    }
}
  • 有一个参数,无返回值
@Test
public void test03(){
    Consumer<String> consumer = (a) -> System.out.println(a);
    consumer.accept("hello!");
}
  • 有一个参数,无返回值(小括号可以省略不写)
@Test
public void test03(){
    Consumer<String> consumer = a -> System.out.println(a);
    consumer.accept("我觉得还行!");
}
  • 有两个及以上的参数,有返回值,并且 Lambda 体中有多条语句
@Test
public void test04(){
    Comparator<Integer> comparator = (a, b) -> {
        System.out.println("比较接口");
        return Integer.compare(a, b);
    };
}
  • 有两个及以上的参数,有返回值,并且 Lambda 体中只有1条语句 (大括号 与 return 都可以省略不写)
@Test
public void test04(){
    Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
}
  • Lambda 表达式 参数的数据类型可以省略不写 Jvm可以自动进行 “类型推断”

1.4 函数式接口

  • Lambda表达式需要“函数式接口”的支持
  • 函数式接口:接口中只有一个抽象方法的接口,称之为函数式接口
  • 定义一个函数式接口:
@FunctionalInterface
public interface MyFun {

    Integer count(Integer a, Integer b);
}
  • 调用:
@Test
public void test05(){
    MyFun myFun1 = (a, b) -> a + b;
    MyFun myFun2 = (a, b) -> a - b;
    MyFun myFun3 = (a, b) -> a * b;
    MyFun myFun4 = (a, b) -> a / b;
}
public Integer operation(Integer a, Integer b, MyFun myFun){
    return myFun.count(a, b);
}

@Test
public void test06(){
    Integer result = operation(1, 2, (x, y) -> x + y);
    System.out.println(result);
}

1.5 案例

案例一:调用 Collections.sort() 方法,通过定制排序 比较两个 Employee (先按照年龄比,年龄相同按照姓名比),使用 Lambda 表达式作为参数传递

  • 代码:
//实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {

    private Integer id;
    private String name;
    private Integer age;
    private Double salary;
}

//定义 List 传入数据

List<Employee> emps = Arrays.asList(
    new Employee(101, "Z3", 19, 9999.99),
    new Employee(102, "L4", 20, 7777.77),
    new Employee(103, "W5", 35, 6666.66),
    new Employee(104, "Tom", 44, 1111.11),
    new Employee(105, "Jerry", 60, 4444.44)
);

//Test
@Test
public void test01(){
    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);
    }
}

案例二:声明函数式接口,接口中声明抽象方法,String getValue(String str); 声明类 TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值;再将一个字符串的第二个和第四个索引位置进行截取字串

@FunctionalInterface
public interface MyInterface {
    String getValue(String str);
}
public static String strHandler(String str,MyInterface mc){
        return mc.getValue(str);
    }
public static void main(String[] args) {
        String s = strHandler("dadsadsad",t -> t.toUpperCase(Locale.ROOT));
        System.out.println(s);
    }

案例三:声明一个带两个泛型的函数式接口,泛型类型为<T, R> T 为参数,R 为返回值;接口中声明对应的抽象方法;在 TestLambda 类中声明方法,使用接口作为参数,计算两个 Long 类型参数的和;在计算两个 Long 类型参数的乘积

@FunctionalInterface
public interface MyInterface<T, R>  {
    R cal(T t1,T t2);
}
public static void op(Long l1, Long l2, MyInterface<Long, Long> mc) {
        System.out.println(mc.cal(l1, l2));
    }
public static void main(String[] args) {
        op(100L,200L,(x,y) -> x + y);
    }

2.函数式接口

  • Java内置四大核心函数式接口:
函数式接口 参数类型 返回类型 用途
Consumer
消费型接口
T void 对类型为T的对象应用操作:void accept(T t)
Supplier
提供型接口
T 返回类型为T的对象:T get()
Function<T, R>
函数型接口
T R 对范型对象应用操作,并返回结果为R类型的对象:R apply(T t)
Predicate
断言型接口
T boolean 确定范型对象是否满足某约束,返回boolean值:boolean test(T t)

2.1消费型接口

@Test
public void test01(){
    //Consumer
    Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
    //test
    consumer.accept(100);
}

2.2提供型接口

@Test
public void test02(){
    List<Integer> list = new ArrayList<>();
    List<Integer> integers = Arrays.asList(1,2,3); 
    list.addAll(integers);
    //Supplier<T>
    Supplier<Integer> supplier = () -> (int)(Math.random() * 10);
    list.add(supplier.get());
    System.out.println(supplier);
    for (Integer integer : list) {
        System.out.println(integer);
    }
}

2.3函数型接口

@Test
public void test03(){
    //Function<T, R>
    String oldStr = "abc123456xyz";
    Function<String, String> function = (s) -> s.substring(1, s.length()-1);
    //test
    System.out.println(function.apply(oldStr));
}

2.4断言型接口

@Test
public void test04(){
    //Predicate<T>
    Integer age = 35;
    Predicate<Integer> predicate = (i) -> i >= 35;
    if (predicate.test(age)){
        System.out.println("你该退休了");
    } else {
        System.out.println("我觉得还OK啦");
    }
}

3.引用

4.1方法引用

  • 定义:若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”
  • 语法格式: 1.对象 :: 实例方法 2.类 :: 静态方法 3.类 :: 实例方法
  • 1.对象::实例方法
@Test
public void test01(){
    PrintStream ps = System.out;
    Consumer<String> con = (s) -> ps.println(s);
    con1.accept("aaa");

    Consumer<String> con1 = ps::println;
    con2.accept("bbb");

    Consumer<String> con2 = System.out::println;
    con2.accept("ccc");

}
  • 注意:Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致

且视他人之疑目如盏盏鬼火,大胆去走自己的夜路。