1.需求分析:
创建一个新的线程,指定线程要执行的任务
package com.wxj.jdk.lambda; public class Demo01Lambda { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("新线程中执行的代码"+Thread.currentThread().getName()); } }).start(); System.out.println("主线程中的代码"+Thread.currentThread().getName()); } }
代码分析:
1.Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心。
2.为了指定这个run方法,不得不需要Runnable的实现类
3.为了省去定义一个Runnable的实现类,不得不使用匿名内部类
4.必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错,
5.而实际上,我们只在乎方法体中的代码
Lambda表达式就是为了去解决这个问题。
2.Lambda表达式的使用
【1】写法:
Lambda表达式是一个匿名函数,可以理解为一段可以传递的代码:
new Thread(()->{ System.out.println("lambda线程表达式"+Thread.currentThread().getName()); }).start();
lambda表达式的优点:简化了匿名内部类的使用,语法更加简单。
匿名内部类语法冗余,体验了Lambda表达式后,发现Lambda表达式是简化匿名内部类的一种方式。
3.Lambda表达式的语法规则
Lambda省去了面向对象的条条框框,Lambda的标准格式由3个部分组成:
(参数类型 参数名称)->{ }
格式说明:
- (参数类型 参数名称):参数列表
- {代码体}:方法体
- ->:箭头,分割参数列表和方法体的
3.1 Lambda练习1
练习无参无返回值的Lambda
定义一个接口:
package com.wxj.jdk.lambda.service; public interface UserService { void show(); }
然后创建主方法使用:
package com.wxj.jdk.lambda; import com.wxj.jdk.lambda.service.UserService; public class Demo03Lambda { public static void main(String[] args) { goShow(new UserService() { @Override public void show() { System.out.println("show方法执行了....."); } }); System.out.println("----------------------"); goShow(()->{ System.out.println("Lambda show 方法执行了"); }); } public static void goShow(UserService userService) { userService.show(); } }
输出:
3.2 Lambda练习2
练习有参有返回值的Lambda表达式案例
创建一个Person对象
package com.wxj.jdk.lambda.domain; import jdk.jfr.DataAmount; public class Person { private String name; private Integer age; private Integer height; 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 Integer getHeight() { return height; } public void setHeight(Integer height) { this.height = height; } public Person(String name, Integer age, Integer height) { this.name = name; this.age = age; this.height = height; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", height=" + height + '}'; } }
然后我们在List集合中保存多个Person对象,然后对这些对象做根据age排序操作
package com.wxj.jdk.lambda; import com.wxj.jdk.lambda.domain.Person; import java.util.*; public class Demo04Lambda { public static void main(String[] args) { List<Person> list = new ArrayList<>(); list.add(new Person("周杰伦",33,175)); list.add(new Person("刘德华",43,175)); list.add(new Person("周星驰",38,175)); list.add(new Person("郭富城",23,175)); Collections.sort(list, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getAge()-o2.getAge(); } }); for (Person person : list) { System.out.println(person); } System.out.println("--------------------"); Collections.sort(list,(Person o1,Person o2)->{ return o1.getAge() - o2.getAge(); }); for (Person person : list) { System.out.println(person); } } }
然后我们发现在sort方法的第二个参数是一个Comparator接口的匿名内部类,且执行的方法有参数和返回值,那么我们可以改写为Lambda表达式。
输出结果:
4.@FunctionalInterface注解
@FunctionalInterface:这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法
@FunctionalInterface public interface UserService{ void show(); }
5.Lambda表达式的原理
匿名内部类的本质是会在编译时生成一个Class文件。XXXXX$1.class
package com.wxj.jdk.lambda; public class Demo01Lambda { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("新线程中执行的代码"+Thread.currentThread().getName()); } }).start(); System.out.println("主线程中的代码"+Thread.currentThread().getName()); System.out.println("-----------------------------"); /*new Thread(()->{ System.out.println("lambda线程表达式"+Thread.currentThread().getName()); }).start();*/ } }
还可以通过反编译工具来查看生成的代码 XJad工具来查看
那么Lambda表达式的原理是什么呢?我们通过JDK自带的一个工具:javap(反编译工具)对字节码进行反汇编操作。
javap -c -p 文件名.class
-c:表示对代码进行反汇编
-p:显示所有的类和成员
在这个反编译的源码中我们看到了一个静态方法lambda$main$0(),这个方法里面做了什么事情呢?
我们可以通过Debug的形式来查看,简单的可以理解成下面的形式:
在这个静态方法中,实现了()->方法体中,方法体的内容。
可以看到这个匿名内部类实现了UserService接口,并重写了show()方法。在show方法中调用了Demo03Lambda.lambda$main$0(),也就是调用了Lambda中的内容。
小结:
匿名内部类在编译的时候会产生一个class文件。
Lambda表达式在程序运行的时候会形成一个类。
1.在类中新增了一个方法,这个方法的方法体就是Lambda表达式中的方法体
2.还会形成一个匿名内部类,实现接口,重写接口中的抽象方法
3.在接口中重写方法会调用新生成的方法
6.Lambda表达式的总结
【1】Lambda表达式的使用前提
Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,使用时注意几个条件:
1.方法的参数或局部变量类型必须为接口才能使用Lambda
2.接口中有且仅有一个抽象方法(@FunctionalInterface)
7.Lambda和匿名内部类的对比(面试题)
Lambda和匿名内部类的对比
- 所需类型不一样
1.匿名内部类的类型可以是类,抽象类,接口
2.Lambda表达式的类型必须是接口
- 抽象方法的数量不一样
1.匿名内部类所需的接口中的抽象方法的数量是随意的
2.Lambda表达式所需的接口中的抽象方法只能有一个
- 实现原理不一样
1.匿名内部类是在编译后形成一个class
2.Lambda表达式是在程序运行的时候动态生成class