Lambda 表达式详解~深入JVM实现原理
讀過上一篇之后,相信對Lambda表達式的語法以及基本原理有了一定了解。對于編寫代碼,有這些知識已經夠用。本文將進一步區分Lambda表達式和匿名內部類在JVM層面的區別,如果對這一部分不感興趣,可以跳過。
經過第一篇的的介紹,我們看到Lambda表達式似乎只是為了簡化匿名內部類書寫,這看起來僅僅通過語法糖在編譯階段把所有的Lambda表達式替換成匿名內部類就可以了。但實時并非如此。在JVM層面,Lambda表達式和匿名內部類有著明顯的差別。
匿名內部類實現
匿名內部類仍然是一個類,只是不需要程序員顯示指定類名,編譯器會自動為該類取名。因此如果有如下形式的代碼,編譯之后將會產生兩個class文件:
public class MainAnonymousClass {public static void main(String[] args) {new Thread(new Runnable(){@Overridepublic void run(){System.out.println("Anonymous Class Thread run()");}}).start();;} }編譯之后文件分布如下,兩個class文件分別是主類和匿名內部類產生的:
進一步分析主類MainAnonymousClass.class的字節碼,可發現其創建了匿名內部類的對象:?
// javap -c MainAnonymousClass.class public class MainAnonymousClass {...public static void main(java.lang.String[]);Code:0: new #2 // class java/lang/Thread3: dup4: new #3 // class MainAnonymousClass$1 /*創建內部類對象*/7: dup8: invokespecial #4 // Method MainAnonymousClass$1."<init>":()V11: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V14: invokevirtual #6 // Method java/lang/Thread.start:()V17: return }Lambda表達式實現
Lambda表達式通過invokedynamic指令實現,書寫Lambda表達式不會產生新的類。如果有如下代碼,編譯之后只有一個class文件:
public class MainLambda {public static void main(String[] args) {new Thread(() -> System.out.println("Lambda Thread run()")).start();;} }編譯之后的結果:
通過javap反編譯命名,我們更能看出Lambda表達式內部表示的不同:?
// javap -c -p MainLambda.class public class MainLambda {...public static void main(java.lang.String[]);Code:0: new #2 // class java/lang/Thread3: dup4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; /*使用invokedynamic指令調用*/9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V12: invokevirtual #5 // Method java/lang/Thread.start:()V15: returnprivate static void lambda$main$0(); /*Lambda表達式被封裝成主類的私有方法*/Code:0: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc #7 // String Lambda Thread run()5: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: return }反編譯之后我們發現Lambda表達式被封裝成了主類的一個私有方法,并通過invokedynamic指令進行調用。
推論,this引用的意義
既然Lambda表達式不是內部類的簡寫,那么Lambda內部的this引用也就跟內部類對象沒什么關系了。在Lambda表達式中this的意義跟在表達式外部完全一樣。因此下列代碼將輸出兩遍Hello Hoolee,而不是兩個引用地址。
public class Hello {Runnable r1 = () -> { System.out.println(this); };Runnable r2 = () -> { System.out.println(toString()); };public static void main(String[] args) {new Hello().r1.run();new Hello().r2.run();}public String toString() { return "Hello Hoolee"; } }總結
以上是生活随笔為你收集整理的Lambda 表达式详解~深入JVM实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爬虫教程( 2 ) --- 爬虫框架 S
- 下一篇: C语言中生成可执行程序的过程