使用 Lambda 表达式的主要原因是:将代码的执行延迟到一个合适的时间点,即调用的时候。 所有的 Lambda 表达式都是延迟执行的。 因为匿名内部类的方法都是要等到调用的时候才会执行。
延迟执行的基本案例:
案例需求:1) 创建一个 Person 接口,有一个 sayHi()的无参无返回值的方法。
2) 在 main 函数中直接实例化 Person 为 p1 对象,并且实现 sayHi()方法,
此时方法并没有执行,因为方法不调用不执行。
3) 在后面输出一句话:主函数执行4) 再调用 sayHi()方法,才发现 sayHi()方法的输出。5) 同理使用 Lambda 也是一样的效果。
案例代码:
public class DemoLazy { public static void main(String[] args) { //这里实例化对象,并没有执行 sayHi 中的方法 Person p1 = new Person() { public void sayHi() { System.out.println("来至内部类的问候"); } }; System.out.println("主函数运行"); //这里才输出 p1.sayHi(); //同理,这里是没有运行方法体的 Person p2 = ()-> System.out.println("来至 Lambda 的问候"); //调用的时候才运行 p2.sayHi(); } } interface Person { void sayHi(); }
有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。 而 Lambda 表达式是延迟执行的,这正好可以作为解决方案,减少无用代码的执行,提升性能。
案例需求:1) 有一个静态方法: void log(int level, String message),当它为日志级别为 1 级的时候,打印出日志的信息。2) 在 main 函数中创建一个时间格式化的类,格式是:yyyy-MM-dd HH:mm:ss.SSS3) 在 main 函数中调用 2 次 log()方法,1 级调用 1 次,2 级调用 1 次。4) 第 1 次日志信息内容是:"Rose: " + sdf.format(new Date()) + " 进行了转账操作"5) 第 2 次日志信息内容是:"Jack: " + sdf.format(new Date()) + " 进行了取钱操作"
public class DemoLazy { public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); log(1, "Rose: " + sdf.format(new Date()) + " 进行了转账操作"); log(2, "Jack: " + sdf.format(new Date()) + " 进行了取钱操作"); } /** - 记录日志 - @param level 日志级别 - @param message 日志信息 */ public static void log(int level, String message) { if (level == 1) { // 用户日志 System.out.println(message); } } }
案例分析:
1) 在 log 方法的内部设置断点,证明两次都会对字符串进行计算。2) 存在问题:字符串拼接完成之后,在调用 log 方法后可能不会被使用,
那么此时之前的拼接工作就白做了,这样就会浪费性能。
Lambda 的更优写法案例分析1) 创建一个接口 BuilderMessage,接口中有一个抽象方法 String buildeMessage(),返回字符串。2) 创建类,创建静态方法用于输出日志信息:static void log(int level, BuilderMessage bulider)在方法内部判断如果是 1 级信息,打印日志信息,并调用接口中的 buildeMessage()方法拼接字符串。3) 在 main 函数中创建日期格式化类,调用 2 次 log()方法:1 级调用 1 次,2 级调用 1 次。log 方法的第 2 个参数使用 Lambda 表达式,方法体中使用 return 返回拼接的字符串。4) 第 1 次信息内容是:"Rose: " + sdf.format(new Date()) + " 进行了转账操作"5) 第 2 次信息内容是:"Jack: " + sdf.format(new Date()) + " 进行了取钱操作"
创建一个拼接字符串的接口,函数式接口
interface BuilderMessage { String buildeMessage(); } public class DemoLazy { 记录日志 - level 日志级别 - bulider 日志信息 */ public static void log(int level, BuilderMessage bulider) { if (level == 1) { // 用户日志 System.out.println(bulider.buildeMessage()); } } public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); log(1, () -> {return "Rose: " + sdf.format(new Date()) + " 进行了转账操作";}); log(2, () -> {return "Jack: " + sdf.format(new Date()) + " 进行了取钱操作";}); } }
证明 Lambda 的延迟执行
案例分析:
在 Lambda 表达式方法体中返回字符串之前,
先输出当前的级别,发现只输出了级别 1,级别 2 并没有输出。
即当级别为 2 时,没有对字符串进行拼接操作了,这样就避免了拼接字符串带来的额外开销。
/** 创建一个拼接字符串的接口,函数式接口 */ interface BuilderMessage { String buildeMessage(); } public class DemoLazy { /** - 记录日志 - @param level 日志级别 - @param bulider 日志信息 */ public static void log(int level, BuilderMessage bulider) { if (level == 1) { // 用户日志 System.out.println(bulider.buildeMessage()); } } public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); log(1, () -> { System.out.println("级别 1"); return "Rose: " + sdf.format(new Date()) + " 进行了转账操作"; }); log(2, () -> { System.out.println("级别 2"); return "Jack: " + sdf.format(new Date()) + " 进行了取钱操作"; }); } }
延迟执行小结:
只有在需要的时候才能运行代码,这是使用 lambda 表达式的一种情况。void log(int level, BuilderMessage bulider) 这段代码实际的运行效果分如下步骤:1) 通过 BuilderMessage 接口接受 lambda 表达式。2) 检查它是否应该被调用,即 level 是否等于 1。3) 在需要的时候调用它,bulider.buildeMessage()。