在Java8提供Lambda表达式后,有些地方可以用比较简洁的Lambda表达式来代替原来相对冗余的匿名内部类了。那么Java在低层实现时,这2种方式有什么区别呢,下面通过例子来分析一下。
1、简单实例 下面列出一个简单的类,类中提供了2 个方式,一个方式用匿名内部类的方式实现;另一个用Lambda表达式的方式实现,即:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package io.github.lzj09.java8.lambda; public class LambdaDemo1 { public static void main(String[] args) { LambdaDemo1 demo = new LambdaDemo1(); demo.anonymousClassMethod(); demo.lambdaFunctionMethod(); } public void anonymousClassMethod() { new Thread(new Runnable() { @Override public void run() { System.out.println("匿名内部类方式执行..."); } }).start(); } public void lambdaFunctionMethod() { new Thread(() -> System.out.println("lambda函数方式执行...")).start(); } }
程序很简单,输出的结果为:
1 2 匿名内部类方式执行... lambda函数方式执行...
2、分析生成的对应的class字节码文件 查看classes文件夹中的字节码文件中,会发现有2个class文件,即LambdaDemo1.class和LambdaDemo1$1.class,如图:
利用javap命令查看这2个class文件对应的汇编指令,首先看看LambdaDemo1$1.class文件,即输入如下命令:
1 javap -c -p LambdaDemo1$1.class
结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Compiled from "LambdaDemo1.java" class io.github.lzj09.java8.lambda.LambdaDemo1$1 implements java.lang.Runnable { final io.github.lzj09.java8.lambda.LambdaDemo1 this$0; io.github.lzj09.java8.lambda.LambdaDemo1$1(io.github.lzj09.java8.lambda.LambdaDemo1); Code: 0: aload_0 1: aload_1 2: putfield #12 // Field this$0:Lio/github/lzj09/java8/lambda/LambdaDemo1; 5: aload_0 6: invokespecial #14 // Method java/lang/Object."<init>":()V 9: return public void run(); Code: 0: getstatic #22 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #28 // String 匿名内部类方式执行... 5: invokevirtual #30 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
从上面的汇编指令大致可以看到LambdaDemo1$1类实现了Runnable接口,并实现run()方法,同时该方法输入一个字符串,即“匿名内部类方式执行…”。
从上可以看出匿名内部类在编译时 ,会生成一个类并实现Runnable接口同时实现run()方法。
再利用javap命令查看LambdaDemo1.class文件,即:
1 javap -c -p LambdaDemo1.class
结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 Compiled from "LambdaDemo1.java" public class io.github.lzj09.java8.lambda.LambdaDemo1 { public io.github.lzj09.java8.lambda.LambdaDemo1(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #1 // class io/github/lzj09/java8/lambda/LambdaDemo1 3: dup 4: invokespecial #16 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #17 // Method anonymousClassMethod:()V 12: aload_1 13: invokevirtual #20 // Method lambdaFunctionMethod:()V 16: return public void anonymousClassMethod(); Code: 0: new #26 // class java/lang/Thread 3: dup 4: new #28 // class io/github/lzj09/java8/lambda/LambdaDemo1$1 7: dup 8: aload_0 9: invokespecial #30 // Method io/github/lzj09/java8/lambda/LambdaDemo1$1."<init>":(Lio/github/lzj09/java8/lambda/LambdaDemo1;)V 12: invokespecial #33 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V 15: invokevirtual #36 // Method java/lang/Thread.start:()V 18: return public void lambdaFunctionMethod(); Code: 0: new #26 // class java/lang/Thread 3: dup 4: invokedynamic #42, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 9: invokespecial #33 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V 12: invokevirtual #36 // Method java/lang/Thread.start:()V 15: return private static void lambda$0(); Code: 0: getstatic #44 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #50 // String lambda函数方式执行... 5: invokevirtual #52 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
从上可以发现,多了一个方法lambda$0(),而该方法内容刚好是Lambda表达式中的内容,即输出“lambda函数方式执行…”。到这其实会有一个疑问,这个lambda$0()方法,在哪里被调用呢?这时需要利用java命令执行时,加入-Djdk.internal.lambda.dumpProxyClasses参数,这样才能真正看到lambda$0()方法调用过程,即:
1 java -Djdk.internal.lambda.dumpProxyClasses io.github.lzj09.java8.lambda.LambdaDemo1
执行该命令后,同样是会输出结果,即:
1 2 匿名内部类方式执行... lambda函数方式执行...
但是查看classes文件,会发现多了一个class文件LambdaDemo1$$Lambda$1.class,如图:
同样用javap命令查看LambdaDemo1$$Lambda$1.class的汇编指令,即:
1 javap -c -p LambdaDemo1$$Lambda$1.class
得到如下结果:
1 2 3 4 5 6 7 8 9 10 11 12 final class io.github.lzj09.java8.lambda.LambdaDemo1$$Lambda$1 implements java.lang.Runnable { private io.github.lzj09.java8.lambda.LambdaDemo1$$Lambda$1(); Code: 0: aload_0 1: invokespecial #10 // Method java/lang/Object."<init>":()V 4: return public void run(); Code: 0: invokestatic #17 // Method io/github/lzj09/java8/lambda/LambdaDemo1.lambda$0:()V 3: return }
可以发现类LambdaDemo1$$Lambda$1实现了Runnable接口,并实现run()方法,同时该方法中就是调用LambdaDemo1类中编译时生成的lambda$0()方法的。
3、总结 Lambda表达式与匿名内部类的低层实现过程还是有些区别的: