1、体验Lambda表达式【理解】
- Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
- Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
- 使用 Lambda 表达式可以使代码变的更加简洁紧凑
- 解析:
- 什么是Lambda?
- Lambda的本质?
- 相对匿名内部类,更加注重行为实现
2、Lambda表达式的标准格式【理解】
-
lambda 表达式的语法格式如下
(形式参数) -> { 代码块 }
():里面可以有参数,也可以没有参数,多个参数,用逗号分隔,
->:用箭头指向后面要做的事情
{ }:包含一段代码,我们称之为代码块,可以看成是方法体中的内容
-
组成Lambda表达式的三要素:形式参数,箭头,代码块
3、Lambda表达式练习1(抽象方法无参无返回值)【理解】
-
Lambda表达式的使用前提
接口中有且仅有一个抽象方法
/** * Lambda表达式练习1(抽象方法无参无返回值) */ public class MainClass { public static void main(String[] args) { useEatable(new Eatable() { @Override public void eat() { System.out.println("一天一顿酒,神仙生活有..."); } }); //lambda就是把方法作为参数传递,就是传递了一个匿名方法作为参数 useEatable(() -> { System.out.println("饭后一颗烟,赛过活神仙..."); }); } public static void useEatable(Eatable eatable) { eatable.eat(); } }
4、Lambda表达式练习2(抽象方法带参无返回值)【应用】
-
接口的抽象方法带参无返回值
-
解析
- 无参无返回值
- () -> {}
- 有参无返回值
- (String s) -> {}
- 无参无返回值
-
代码演示
/** * Lambda表达式练习2(抽象方法带参无返回值) */ public class MainClass { public static void main(String[] args) { //匿名内部类方式 useFlyable(new Flyable() { @Override public void fly(String s) { System.out.println(s); System.out.println("飞机自驾游"); } }); System.out.println("----------------------"); //Lambda方式,(s)中必须传递形参 useFlyable((s) -> { System.out.println(s); }); } public static void useFlyable(Flyable flyable) { flyable.fly("风和日丽,晴空万里"); } }
5、Lambda表达式练习3(抽象方法带参带返回值)【应用】
-
接口抽象方法带参带返回值
-
解析
- 无参无返回值
- () -> {}
- 有参无返回值
- (形参,形参) -> {}
- 有参有返回值
- (形参,形参) -> {return }
- 无参无返回值
-
示例代码
/** * Lambda表达式练习3(抽象方法带参带返回值) */ public class MainClass { public static void main(String[] args) { useAddable((int x, int y) -> { return x + y; }); } public static void useAddable(Addable addable) { int num = addable.add(10, 20); System.out.println(num); } }
6、Lambda表达式的省略模式【应用】
-
省略规则:
参数类型可以省略。但是有多个参数的情况下,不能只省略一个
如果参数有且仅有一个,那么小括号可以省略
如果代码块的语句只有一条,可以省略大括号和分号,甚至是return
-
解析
- (int x,int y) -> {} (x,y) ->{}
- (s) -> {} s-> {}
- () -> {只有一句;} () -> 只有一句
- () -> {return 只有一句;} () -> 只有一句
-
示例代码
/** * Lambda表达式的省略模式 */ public class MainClass { public static void main(String[] args) { //参数类型可以省略。但是有多个参数的情况下,不能只省略一个 useAddable((x,y) -> { return x + y; }); //如果参数有且仅有一个,那么小括号可以省略 useFlyable(s -> {System.out.println(s);}); //如果代码块的语句只有一条,可以省略大括号和分号,甚至是return useAddable((x,y) -> x + y); } public static void useAddable(Addable addable) { int num = addable.add(10, 20); System.out.println(num); } public static void useFlyable(Flyable flyable) { flyable.fly("风和日丽,晴空万里"); } }
7、Lambda表达式的注意事项【理解】
-
注意事项:
使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
必须有上下文环境,才能推导出Lambda对应的接口
根据局部变量的赋值得知Lambda对应的接口:Runnable r = () -> System.out.println(“Lambda表达式”);
根据调用方法的参数得知Lambda对应的接口:new Thread(() -> System.out.println(“Lambda表达式”)).start();
-
解析
- 没有上下文环境就没法推断
8、Lambda表达式和匿名内部类的区别【理解】
-
所需类型不同
-
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
-
Lambda表达式:只能是接口
-
-
使用限制不同
-
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
-
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
-
-
实现原理不同
-
匿名内部类:编译之后,产生一个单独的.class字节码文件
-
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
-
9、接口组成更新概述【理解】
-
接口的组成
-
常量
- public static final
-
抽象方法
- public abstract
- 默认方法(Java 8) (新增)
- 静态方法(Java 8) (新增)
- 私有方法(Java 9) (新增)
-
-
解析
- default
- static
- private
-
再谈接口组成
10、接口中默认方法【应用】
-
接口默认方法的意义
- 方便接口扩展功能,而不影响原有实现类
-
接口中默认方法的定义格式:
格式:
public default 返回值类型 方法名(参数列表) { }
- public可以省略,default不能省略
-
接口中默认方法的注意事项:
默认方法不是抽象方法,所有有方法体,也就是有具体的实现。
默认方法不是抽象方法,所以不强制实现类去重写。
实现类可以重写接口默认方法,重写的时候去掉default关键字,也可以直接使用接口默认方法。
-
解析
-
default方法的好处?
解耦,不要求所有实现类重写default方法
-
-
代码演示
/** * 包含默认方法的接口 */ public interface IInter { void eat(); void jump(); default void fly() { System.out.println("飞..."); } } /** * 实现默认方法接口类 */ public class Cat implements IInter { @Override public void eat() { System.out.println("吃鱼..."); } @Override public void jump() { System.out.println("跳树..."); } //重写默认方法 @Override public void fly() { System.out.println("会飞的机器猫..."); } }
11、接口中静态方法【应用】
-
接口中静态方法的定义格式:
格式:
public static 返回值类型 方法名(参数列表) { }
-
接口中静态方法的注意事项:
-
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
-
public可以省略,static不能省略
-
-
代码演示
public interface Eatable { void eat(); //接口静态方法 static void method() { System.out.println("接口的静态方法..."); } } /** * 接口中静态方法 */ public class MainClass { public static void main(String[] args) { Cat cat = new Cat(); //实现接口的对象无法调用接口静态方法 //cat.method(); //接口静态方法的调用需要使用接口 Eatable.method(); } }
12、接口中私有方法【应用】
-
接口中私有方法的定义格式:
格式1:
private 返回值类型 方法名(参数列表) { }
格式2:
private static 返回值类型 方法名(参数列表) { }
-
接口中私有方法的注意事项:
- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
-
解析
- 私有方法不能被外部访问,为什么还需要私有方法?
- 复用,易维护
- 私有方法不能被外部访问,为什么还需要私有方法?
-
代码演示
public interface Inter { //私有方法 private static void privateShow() { System.out.println("这里的大山不一般,有花有草我喜欢。"); System.out.println("有花有草我喜欢啊,这里的大山不一般!"); } //默认方法 default void defaultShow() { System.out.println("defaultShow开始"); //使用接口私有方法 privateShow(); System.out.println("defaultShow结束"); } //静态方法 static void staticShow() { System.out.println("defaultShow开始"); //使用接口私有方法 privateShow(); System.out.println("defaultShow结束"); } }
13、体验方法引用【理解】
-
所有方法引用的地方,都可以使用lambda,所有lambda的地方都可以使用匿名内部类,所有匿名内部类,都可以使用实现类替换
-
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?
-
方法引用是对Lambda的省略,和已有方法的复用
14、方法引用符【应用】
-
方法引用符
:: 该符号为引用运算符,而它所在的表达式被称为方法引用
-
回顾一下我们在体验方法引用中的代码
-
Lambda表达式:usePrintable(s -> System.out.println(s));
- 分析:拿到参数 s 之后通过Lambda表达式,传递给 System.out.println 方法去处理
-
方法引用:usePrintable(System.out::println);
- 分析:直接使用 System.out 中的 println 方法来取代Lambda,代码更加的简洁
-
-
推导与省略
-
如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
-
如果使用方法引用,也是同样可以根据上下文进行推导
-
方法引用是Lambda的孪生兄弟,是对Lambda的省略和已有方法的复用
-
15、引用类方法【应用】
-
常见的引用方式:
- 引用类方法–静态方法引用
- 引用对象的实例方法–实例方法引用
- 引用类的实例方法
- 引用构造器
-
引用类方法,其实就是引用类的静态方法
格式:类名 :: 静态方法
范例:Integer:: parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据
-
代码演示
/** * 引用类方法 */ public class MainClass { public static void main(String[] args) { //Lambda方式 useConvert((s) -> Integer.parseInt(s)); //静态方法引用 useConvert(Integer::parseInt); } private static void useConvert(Convertable convertable) { int num = convertable.convert("666"); System.out.println(num); } } interface Convertable { int convert(String str); }
16、引用对象的实例方法【应用】
-
引用对象的实例方法,其实就引用类中的成员方法
格式:对象::成员方法
范例: “HelloWorld”::toUpperCase
String类中的方法:public String toUpperCase() 将此String所有字符转换为大写
-
代码演示
/** * 引用对象的实例方法 */ public class MainClass { public static void main(String[] args) { //采用Lambda usePrinter((s) -> { System.out.println(s.toUpperCase());}); //采用对象的实例方法引用 usePrinter(new PrintString()::printUpper); } private static void usePrinter(Printer printer) { printer.printUpperCase("hello word"); } } class PrintString { public void printUpper(String s) { String result = s.toUpperCase(); System.out.println(result); } public void printLower(String s) { String result = s.toLowerCase(); System.out.println(result); } } interface Printer { void printUpperCase(String s); }
17、引用类的实例方法
-
引用类的实例方法,其实就是引用类中的成员方法
-
格式:类名::成员方法
范例:String::substring
String类中的方法: public String substring(int beginIndex,int endIndex)
从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex
/** * 引用类的实例方法 */ public class MainClass { public static void main(String[] args) { useMyString((s,x,y) -> s.substring(x,y)); //第一个参数是调用者,后面的参数是方法参数 useMyString(String::substring); } private static void useMyString(MyString myString) { String str = myString.subString("HelloWord" , 3, 5); System.out.println(str); } } interface MyString { String subString(String str, int x, int y); }
18、引用构造器
-
引用构造器,其实就是引用构造方法
- 格式:类名 :: new
- 范例:Student::new
-
代码演示
/** * 引用构造器 */ public class MainClass { public static void main(String[] args) { //使用Lambda useStudentBuilder((name,age) -> {return new Student(name,age);}); //使用引用构造 useStudentBuilder(Student::new); } private static void useStudentBuilder(StudentBuilder studentBuilder) { Student student = new Student("李逵",20); System.out.println(student); } } /** * 接口 */ interface StudentBuilder { Student buildStudent(String name, int age); } /** * 学生类 */ class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }