Java編譯生成多個(gè).class文件的原理和作用
下面作為一名經(jīng)驗(yàn)豐富的開發(fā)者,在Java項(xiàng)目中執(zhí)行編譯后,可能會發(fā)現(xiàn)一個(gè).java源文件有時(shí)會產(chǎn)生多個(gè).class文件。從技術(shù)實(shí)現(xiàn)層面詳細(xì)剖析這一現(xiàn)象。
一、內(nèi)部類機(jī)制與.class文件生成
成員內(nèi)部類(常規(guī)內(nèi)部類)
// Outer.java public class Outer { public class Inner { void display() { System.out.println("Inner class"); } } }
編譯后將生成:
Outer.class
Outer$Inner.class
實(shí)現(xiàn)原理:
- 編譯器會為內(nèi)部類生成獨(dú)立.class文件
- 內(nèi)部類會隱式持有外部類的引用(通過合成構(gòu)造函數(shù)參數(shù))
- 訪問外部類私有成員是通過編譯器生成的訪問器方法(synthetic accessor)
局部內(nèi)部類(方法內(nèi)部類)
public class Outer { void method() { class LocalInner { void show() { System.out.println("Local inner"); } } new LocalInner().show(); } }
生成文件:
Outer.class
Outer$1LocalInner.class
(數(shù)字前綴表示定義順序)
特點(diǎn):
- 類名包含定義它的方法信息
- 無法從方法外部訪問
- 會捕獲方法的final局部變量
匿名內(nèi)部類
public class Outer { Runnable r = new Runnable() { @Override public void run() { System.out.println("Anonymous"); } }; }
生成文件:
Outer.class
Outer$1.class
實(shí)現(xiàn)細(xì)節(jié):
- 類名使用數(shù)字序號而非具體名稱
- 每個(gè)匿名類都會生成獨(dú)立.class文件
- 會隱式實(shí)現(xiàn)指定接口或繼承指定類
二、Lambda表達(dá)式的特殊處理
public class LambdaExample { public static void main(String[] args) { Runnable r = () -> System.out.println("Lambda"); r.run(); } }
生成文件可能包括:
LambdaExample.class
LambdaExample$$Lambda$1.class
(具體名稱取決于JVM實(shí)現(xiàn))
底層機(jī)制:
- Java 7引入的
invokedynamic
指令 - 使用
LambdaMetafactory
動態(tài)生成實(shí)現(xiàn)類 - 現(xiàn)代JVM通常不會生成物理.class文件,而是在運(yùn)行時(shí)動態(tài)生成字節(jié)碼
三、枚舉類型的編譯處理
public enum Color { RED, GREEN, BLUE; }
生成文件:
Color.class
Color$1.class
(可能包含枚舉相關(guān)輔助信息)
枚舉編譯特點(diǎn):
- 每個(gè)枚舉常量都是枚舉類的實(shí)例
- 編譯器生成
values()
和valueOf()
方法 - 可能生成額外的輔助類處理枚舉序列化等特性
四、編譯器生成的合成類
橋接方法(Bridge Methods)
class Parent<T> { void set(T t) { /*...*/ } } class Child extends Parent<String> { @Override void set(String s) { /*...*/ } }
生成:
Parent.class
Child.class
- 可能包含橋接方法相關(guān)的合成類
類型擦除輔助類
泛型類型擦除后,編譯器可能生成輔助類保證類型安全
五、技術(shù)驗(yàn)證方法
使用javap反編譯
javap -v Outer$Inner.class
查看合成成員
javap -p Outer.class | grep synthetic
分析字節(jié)碼
javac -g:none -XD-printflat -XD-printsource Outer.java
六、實(shí)際開發(fā)注意事項(xiàng)
類加載影響:
內(nèi)部類不會自動隨外部類加載
反射時(shí)需要特別注意$
符號的處理
序列化考慮:
匿名類和局部類無法被序列化
內(nèi)部類序列化會連帶序列化外部類實(shí)例
構(gòu)建工具處理:
<!-- Maven配置示例 --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> </plugin> </plugins> </build>
調(diào)試支持:
調(diào)試信息會包含內(nèi)部類源位置映射
匿名類的堆棧跟蹤顯示數(shù)字編號
七、性能與設(shè)計(jì)考量
類加載開銷:
每個(gè).class文件都需要單獨(dú)加載
大量匿名類可能增加PermGen/Metaspace壓力
設(shè)計(jì)替代方案:
// 替代匿名類的lambda Runnable r = () -> System.out.println("Better"); // 替代內(nèi)部類的靜態(tài)嵌套類 public static class StaticNested { ... }
模塊化影響:
JPMS模塊系統(tǒng)中需要顯式導(dǎo)出嵌套類
反射訪問內(nèi)部類需要--add-opens
參數(shù)
總結(jié)
到此這篇關(guān)于Java編譯生成多個(gè).class文件的原理和作用的文章就介紹到這了,更多相關(guān)Java編譯生成多個(gè).class文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring 應(yīng)用中集成 Apache Shiro的方法
這篇文章主要介紹了Spring 應(yīng)用中集成 Apache Shiro的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05淺談SpringCloud實(shí)現(xiàn)簡單的微服務(wù)架構(gòu)
Spring Cloud是一系列框架的有序集合,本文就使用SpringCloud實(shí)現(xiàn)一套簡單的微服務(wù)架構(gòu),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01springboot2.0 配置時(shí)間格式化不生效問題的解決
這篇文章主要介紹了springboot2.0 配置時(shí)間格式化不生效問題的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Spring?Cloud?Gateway遠(yuǎn)程命令執(zhí)行漏洞分析(CVE-2022-22947)
使用Spring Cloud Gateway的應(yīng)用程序在Actuator端點(diǎn)啟用、公開和不安全的情況下容易受到代碼注入的攻擊,攻擊者可以惡意創(chuàng)建允許在遠(yuǎn)程主機(jī)上執(zhí)行任意遠(yuǎn)程執(zhí)行的請求,這篇文章主要介紹了Spring?Cloud?Gateway遠(yuǎn)程命令執(zhí)行漏洞(CVE-2022-22947),需要的朋友可以參考下2023-03-03