Java 8新特性简介
- 速度更快
- 代码更少(增加了新的语法:Lambda 表达式)
- 强大的 Stream API
- 便于并行
- 最大化减少空指针异常:Optional
- Nashorn引擎,允许在JVM上运行JS应用
Lambda 表达式
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以 传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更 灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了 提升
Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操 作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符 或箭头操作符。
它将 Lambda 分为两个部分:
左侧:指定了 Lambda 表达式需要的参数列表
右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的功能。
Lambda表达式的使用
1.举例: (o1,o2) -> Integer.compare(o1,o2);
2.格式:
-> :lambda操作符 或 箭头操作符
->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)
->右边:lambda体 (其实就是重写的抽象方法的方法体)
3.Lambda表达式的使用:(分为6种情况介绍)
总结:
->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略
->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字
4.Lambda表达式的本质:作为函数式接口的实例
5. 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解,
这样做可以检查它是否是一个函数式接口。
6. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
从匿名类到 Lambda 的转换举例
1 public class LambdaTest { 2 3 @Test 4 public void test1(){ 5 Runnable r1 = new Runnable() { 6 public void run() { 7 System.out.println("我爱北京天安门"); 8 } 9 }; 10 r1.run(); 11 12 System.out.println("***********************"); 13 14 Runnable r2 = () -> System.out.println("我爱北京故宫"); 15 r2.run(); 16 } 17 18 @Test 19 public void test2(){ 20 Comparator<Integer> com1 = new Comparator<Integer>() { 21 @Override 22 public int compare(Integer o1, Integer o2) { 23 return Integer.compare(o1, o2); 24 } 25 }; 26 27 int compare1 = com1.compare(12, 21); 28 System.out.println(compare1); 29 30 System.out.println("***********************"); 31 32 //Lambda表达式的写法 33 Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2); 34 System.out.println(com2.compare(32, 21)); 35 36 System.out.println("***********************"); 37 38 //方法引用 39 Comparator<Integer> com3 = Integer::compareTo; 40 System.out.println(com3.compare(32, 21)); 41 } 42 }
Lambda表达式的使用举例
1 /** 2 * Lambda表达式的使用 3 * 4 * 1.举例: (o1,o2) -> Integer.compare(o1,o2); 5 * 2.格式: 6 * -> :lambda操作符 或 箭头操作符 7 * ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表) 8 * ->右边:lambda体 (其实就是重写的抽象方法的方法体) 9 * 10 * 3. Lambda表达式的使用:(分为6种情况介绍) 11 * 12 * 总结: 13 * ->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略 14 * ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字 15 * 16 * 4.Lambda表达式的本质:作为函数式接口的实例 17 * 18 * 5. 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解, 19 * 这样做可以检查它是否是一个函数式接口。 20 * 21 * 6. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。 22 * 23 */ 24 public class LambdaTest1 { 25 26 //语法格式一:无参,无返回值 27 @Test 28 public void test1(){ 29 Runnable r1 = new Runnable() { 30 @Override 31 public void run() { 32 System.out.println("我爱北京天安门"); 33 } 34 }; 35 36 r1.run(); 37 38 System.out.println("***********************"); 39 40 Runnable r2 = () -> { 41 System.out.println("我爱北京故宫"); 42 }; 43 44 r2.run(); 45 } 46 47 //语法格式二:Lambda 需要一个参数,但是没有返回值。 48 @Test 49 public void test2(){ 50 Consumer<String> con = new Consumer<String>() { 51 @Override 52 public void accept(String s) { 53 System.out.println(s); 54 } 55 }; 56 57 con.accept("谎言和誓言的区别是什么?"); 58 59 System.out.println("***********************"); 60 61 Consumer<String> con1 = (String s) -> { 62 System.out.println(s); 63 }; 64 65 con1.accept("一个是听的人当真了,一个是说的人当真了"); 66 } 67 68 //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断” 69 @Test 70 public void test3(){ 71 Consumer<String> con1 = (String s) -> { 72 System.out.println(s); 73 }; 74 75 con1.accept("一个是听的人当真了,一个是说的人当真了"); 76 77 System.out.println("***********************"); 78 79 Consumer<String> con2 = (s) -> { 80 System.out.println(s); 81 }; 82 83 con2.accept("一个是听的人当真了,一个是说的人当真了"); 84 } 85 86 @Test 87 public void test4(){ 88 ArrayList<String> list = new ArrayList<>();// 类型推断 89 int[] arr = {1, 2, 3};// 类型推断 90 } 91 92 //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略 93 @Test 94 public void test5(){ 95 Consumer<String> con1 = (s) -> { 96 System.out.println(s); 97 }; 98 99 con1.accept("一个是听的人当真了,一个是说的人当真了"); 100 101 System.out.println("***********************"); 102 103 Consumer<String> con2 = s -> { 104 System.out.println(s); 105 }; 106 107 con2.accept("一个是听的人当真了,一个是说的人当真了"); 108 } 109 110 //语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值 111 @Test 112 public void test6(){ 113 Comparator<Integer> com1 = new Comparator<Integer>() { 114 @Override 115 public int compare(Integer o1, Integer o2) { 116 System.out.println(o1); 117 System.out.println(02); 118 return o1.compareTo(o2); 119 } 120 }; 121 122 System.out.println(com1.compare(12, 21)); 123 124 System.out.println("***********************"); 125 126 Comparator<Integer> com2 = (o1, o2) -> { 127 System.out.println(o1); 128 System.out.println(02); 129 return o1.compareTo(o2); 130 }; 131 132 System.out.println(com2.compare(12, 6)); 133 } 134 135 //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略 136 @Test 137 public void test7(){ 138 Comparator<Integer> com1 = (o1, o2) -> { 139 return o1.compareTo(o2); 140 }; 141 System.out.println(com1.compare(12, 6)); 142 143 System.out.println("***********************"); 144 145 Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2); 146 System.out.println(com2.compare(12, 21)); 147 } 148 149 @Test 150 public void test8(){ 151 Consumer<String> con1 = s -> { 152 System.out.println(s); 153 }; 154 155 con1.accept("一个是听的人当真了,一个是说的人当真了"); 156 157 System.out.println("***********************"); 158 159 Consumer<String> con2 = s -> System.out.println(s); 160 con2.accept("一个是听的人当真了,一个是说的人当真了"); 161 } 162 163 164 }
函数式(Functional)接口
-
只包含一个抽象方法的接口,称为函数式接口。
-
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
-
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
-
在java.util.function包下定义了Java 8 的丰富的函数式接口
自定义函数式接口
1 /** 2 * 自定义函数式接口 3 */ 4 @FunctionalInterface 5 public interface MyInterface { 6 7 void method1(); 8 9 // void method2(); 10 }
Java 内置四大核心函数式接口
其他函数式接口
函数式接口举例
1 /** 2 * java内置的4大核心函数式接口 3 * <p> 4 * 消费型接口 Consumer<T> void accept(T t) 5 * 供给型接口 Supplier<T> T get() 6 * 函数型接口 Function<T,R> R apply(T t) 7 * 断定型接口 Predicate<T> boolean test(T t) 8 */ 9 public class LambdaTest2 { 10 11 // 消费型接口 Consumer<T> void accept(T t) 12 @Test 13 public void test1() { 14 happyTime(500, new Consumer<Double>() { 15 @Override 16 public void accept(Double aDouble) { 17 System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble); 18 } 19 }); 20 21 System.out.println("********************"); 22 23 happyTime(400, money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money)); 24 25 } 26 27 public void happyTime(double money, Consumer<Double> con) { 28 con.accept(money); 29 } 30 31 32 @Test 33 public void test2() { 34 List<String> list = Arrays.asList("北京", "南京", "天津", "东京", "西京", "普京"); 35 36 List<String> filterStrs = filterString(list, new Predicate<String>() { 37 @Override 38 public boolean test(String s) { 39 return s.contains("京"); 40 } 41 }); 42 43 System.out.println(filterStrs); 44 45 List<String> filterStrs1 = filterString(list, s -> s.contains("京")); 46 System.out.println(filterStrs1); 47 } 48 49 //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定 50 public List<String> filterString(List<String> list, Predicate<String> pre) { 51 ArrayList<String> filterList = new ArrayList<>(); 52 for (String s : list) { 53 if(pre.test(s)) { 54 filterList.add(s); 55 } 56 } 57 return filterList; 58 } 59 }
方法引用(Method References)
-
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
-
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就 是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向 一个方法,可以认为是Lambda表达式的一个语法糖。
-
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的 方法的参数列表和返回值类型保持一致!
-
格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
-
如下三种主要使用情况:
对象::实例方法名 类::静态方法名 类::实例方法名
方法引用的使用
1 /** 2 * 方法引用的使用 3 * 4 * 1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用! 5 * 6 * 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以 7 * 方法引用,也是函数式接口的实例。 8 * 9 * 3. 使用格式: 类(或对象) :: 方法名 10 * 11 * 4. 具体分为如下的三种情况: 12 * 情况1 对象 :: 非静态方法 13 * 情况2 类 :: 静态方法 14 * 15 * 情况3 类 :: 非静态方法 16 * 17 * 5. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的 18 * 形参列表和返回值类型相同!(针对于情况1和情况2) 19 * 20 */ 21 public class MethodRefTest { 22 23 // 情况一:对象 :: 实例方法 24 //Consumer中的void accept(T t) 25 //PrintStream中的void println(T t) 26 @Test 27 public void test1(){ 28 Consumer<String> con1 = str -> System.out.println(str); 29 con1.accept("北京"); 30 31 System.out.println("*******************"); 32 33 Consumer<String> con2 = System.out :: println; 34 con2.accept("beijing"); 35 } 36 37 //Supplier中的T get() 38 //Employee中的String getName() 39 @Test 40 public void test2(){ 41 Employee emp = new Employee(1001,"Tom",23,5600); 42 43 Supplier<String> sup1 = () -> emp.getName(); 44 System.out.println(sup1.get()); 45 46 System.out.println("*******************"); 47 48 Supplier<String> sup2 = emp::getName; 49 System.out.println(sup2.get()); 50 } 51 52 53 // 情况二:类 :: 静态方法 54 //Comparator中的int compare(T t1,T t2) 55 //Integer中的int compare(T t1,T t2) 56 @Test 57 public void test3(){ 58 Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2); 59 System.out.println(com1.compare(12, 21)); 60 61 System.out.println("*******************"); 62 63 Comparator<Integer> com2 = Integer::compare; 64 System.out.println(com2.compare(12, 8)); 65 66 } 67 68 //Function中的R apply(T t) 69 //Math中的Long round(Double d) 70 @Test 71 public void test4(){ 72 Function<Double, Long> func = new Function<Double, Long>() { 73 @Override 74 public Long apply(Double aDouble) { 75 return Math.round(aDouble); 76 } 77 }; 78 System.out.println(func.apply(12.3)); 79 80 System.out.println("*******************"); 81 82 Function<Double, Long> func1 = d -> Math.round(d); 83 System.out.println(func1.apply(12.6)); 84 85 System.out.println("*******************"); 86 87 Function<Double, Long> func2 = Math::round; 88 System.out.println(func2.apply(12.7)); 89 } 90 91 // 情况三:类 :: 实例方法 (有难度) 92 // Comparator中的int comapre(T t1,T t2) 93 // String中的int t1.compareTo(t2) 94 @Test 95 public void test5(){ 96 Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2); 97 System.out.println(com1.compare("abc", "abd")); 98 99 System.out.println("*******************"); 100 101 Comparator<String> com2 = String::compareTo; 102 System.out.println(com2.compare("abc", "abe")); 103 } 104 105 //BiPredicate中的boolean test(T t1, T t2); 106 //String中的boolean t1.equals(t2) 107 @Test 108 public void test6(){ 109 BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2); 110 System.out.println(pre1.test("abc", "abc")); 111 112 System.out.println("*******************"); 113 114 BiPredicate<String, String> pre2 = String::equals; 115 System.out.println(pre2.test("abc", "abd")); 116 } 117 118 // Function中的R apply(T t) 119 // Employee中的String getName(); 120 @Test 121 public void test7(){ 122 Employee employee = new Employee(1001, "Jerry", 23, 6000); 123 124 Function<Employee, String> func1 = e -> e.getName(); 125 System.out.println(func1.apply(employee)); 126 127 System.out.println("*******************"); 128 129 Function<Employee, String> func2 = Employee::getName; 130 System.out.println(func2.apply(employee)); 131 } 132 }
构造器引用
与函数式接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象 方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
构造器引用使用
1 /** 2 * 一、构造器引用 3 * 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。 4 * 抽象方法的返回值类型即为构造器所属的类的类型 5 * 6 * 二、数组引用 7 * 大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。 8 * 9 * 三、格式 10 * 类(或数组) :: new 11 * 12 */ 13 public class ConstructorRefTest { 14 15 //构造器引用 16 //Supplier中的T get() 17 //Employee的空参构造器:Employee() 18 @Test 19 public void test1(){ 20 Supplier<Employee> sup = new Supplier<Employee>() { 21 @Override 22 public Employee get() { 23 return new Employee(); 24 } 25 }; 26 System.out.println("*******************"); 27 28 Supplier<Employee> sup1 = () -> new Employee(); 29 System.out.println(sup1.get()); 30 31 System.out.println("*******************"); 32 33 Supplier<Employee> sup2 = Employee::new; 34 System.out.println(sup2.get()); 35 } 36 37 //Function中的R apply(T t) 38 @Test 39 public void test2(){ 40 Function<Integer, Employee> func1 = id -> new Employee(id); 41 System.out.println(func1.apply(1001)); 42 43 System.out.println("*******************"); 44 45 Function<Integer, Employee> func2 = Employee::new; 46 System.out.println(func2.apply(1002)); 47 } 48 49 //BiFunction中的R apply(T t,U u) 50 @Test 51 public void test3(){ 52 BiFunction<Integer, String, Employee> func1 = (id, name) -> new Employee(id, name); 53 System.out.println(func1.apply(1001, "Tom")); 54 55 System.out.println("*******************"); 56 57 BiFunction<Integer, String, Employee> func2 = Employee::new; 58 System.out.println(func2.apply(1002, "Tom")); 59 } 60 61 //数组引用 62 //Function中的R apply(T t) 63 @Test 64 public void test4(){ 65 Function<Integer, String[]> func1 = length -> new String[length]; 66 String[] arr1 = func1.apply(5); 67 System.out.println(Arrays.toString(arr1)); 68 69 System.out.println("*******************"); 70 71 Function<Integer, String[]> func2 = String[]::new; 72 String[] arr2 = func2.apply(6); 73 System.out.println(Arrays.toString(arr2)); 74 } 75 }