面試官:說說反射的底層實現(xiàn)原理?
反射是 Java 面試中必問的面試題,但只有很少人能真正的理解“反射”并講明白反射,更別說能說清楚它的底層實現(xiàn)原理了。所以本文就通過大白話的方式來系統(tǒng)的講解一下反射,希望大家看完之后能真正的理解并掌握“反射”這項技術(shù)。
1.什么是反射?
反射在程序運行期間動態(tài)獲取類和操縱類的一種技術(shù)。
2.反射的應(yīng)用有哪些?
反射在日常開發(fā)中使用的地方有很多,例如以下幾個:
- 動態(tài)代理:反射是動態(tài)代理的底層實現(xiàn),即在運行時動態(tài)地創(chuàng)建代理對象,并攔截和增強方法調(diào)用。這常用于實現(xiàn) AOP 功能,如日志記錄、事務(wù)管理等。
- Bean 創(chuàng)建:Spring/Spring Boot 項目中,在項目啟動時,創(chuàng)建的 Bean 對象就是通過反射來實現(xiàn)的。
- JDBC 連接:JDBC 中的 DriverManager 類通過反射加載并注冊數(shù)據(jù)庫驅(qū)動,這是 Java 數(shù)據(jù)庫連接的標(biāo)準(zhǔn)做法。
3.反射實現(xiàn)
反射的關(guān)鍵實現(xiàn)方法有以下幾個:
- 得到類:Class.forName("類名")
- 得到所有字段:getDeclaredFields()
- 得到所有方法:getDeclaredMethods()
- 得到構(gòu)造方法:getDeclaredConstructor()
- 得到實例:newInstance()
- 調(diào)用方法:invoke()
具體使用示例如下:
// 1.反射得到對象
Class<?> clazz = Class.forName("User");
// 2.得到方法
Method method = clazz.getDeclaredMethod("publicMethod");
// 3.得到靜態(tài)方法
Method staticMethod = clazz.getDeclaredMethod("staticMethod");
// 4.執(zhí)行靜態(tài)方法
staticMethod.invoke(clazz);
反射執(zhí)行私有方法代碼實現(xiàn)如下:
// 1.反射得到對象
Class<?> clazz = Class.forName("User");
// 2.得到私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
// 3.設(shè)置私有方法可訪問
privateMethod.setAccessible(true);
// 4.得到實例
Object user = clazz.getDeclaredConstructor().newInstance();
// 5.執(zhí)行私有方法
privateMethod.invoke(user);
4.底層實現(xiàn)原理
從上述內(nèi)容可以看出,對于反射來說,操縱類最主要的方法是 invoke,所以搞懂了 invoke 方法的實現(xiàn),也就搞定了反射的底層實現(xiàn)原理了。
invoke 方法的執(zhí)行流程如下:
- 查找方法:當(dāng)通過 java.lang.reflect.Method 對象調(diào)用 invoke 方法時,Java 虛擬機(JVM)首先確認(rèn)該方法是否存在并可以訪問。這包括檢查方法的訪問權(quán)限、方法簽名是否匹配等。
- 安全檢查:如果方法是私有的或受保護的,還需要進行訪問權(quán)限的安全檢查。如果當(dāng)前調(diào)用者沒有足夠的權(quán)限訪問這個方法,將拋出 IllegalAccessException。
- 參數(shù)轉(zhuǎn)換和適配:invoke 方法接受一個對象實例和一組參數(shù),需要將這些參數(shù)轉(zhuǎn)換成對應(yīng)方法簽名所需要的類型,并且進行必要的類型檢查和裝箱拆箱操作。
- 方法調(diào)用:對于非私有方法,Java 反射實際上是通過 JNI(Java Native Interface,Java 本地接口)調(diào)用到 JVM 內(nèi)部的 native 方法,例如 java.lang.reflect.Method.invoke0()。這個 native 方法負(fù)責(zé)完成真正的動態(tài)方法調(diào)用。對于 Java 方法,JVM 會通過方法表、虛方法表(vtable)進行查找和調(diào)用;對于非虛方法或者靜態(tài)方法,JVM 會直接調(diào)用相應(yīng)的方法實現(xiàn)。
- 異常處理:在執(zhí)行方法的過程中,如果出現(xiàn)任何異常,JVM 會捕獲并將異常包裝成 InvocationTargetException 拋出,應(yīng)用程序可以通過這個異常獲取到原始異常信息。
- 返回結(jié)果:如果方法正常執(zhí)行完畢,invoke 方法會返回方法的執(zhí)行結(jié)果,或者如果方法返回類型是 void,則不返回任何值。
通過這種方式,Java 反射的 invoke 方法能夠打破編譯時的綁定,實現(xiàn)運行時動態(tài)調(diào)用對象的方法,提供了極大的靈活性,但也帶來了運行時性能損耗和安全隱患(如破壞封裝性、違反訪問控制等)。
5.優(yōu)缺點分析
反射的優(yōu)點如下:
- 靈活性:使用反射可以在運行時動態(tài)加載類,而不需要在編譯時就將類加載到程序中。這對于需要動態(tài)擴展程序功能的情況非常有用。
- 可擴展性:使用反射可以使程序更加靈活和可擴展,同時也可以提高程序的可維護性和可測試性。
- 實現(xiàn)更多功能:許多框架都使用反射來實現(xiàn)自動化配置和依賴注入等功能。例如,Spring 框架就使用反射來實現(xiàn)依賴注入。
反射的缺點如下:
- 性能問題:使用反射會帶來一定的性能問題,因為反射需要在運行時動態(tài)獲取類的信息,這比在編譯時就獲取信息要慢。
- 安全問題:使用反射可以訪問和修改類的字段和方法,這可能會導(dǎo)致安全問題。因此,在使用反射時需要格外小心,確保不會對程序的安全性造成影響。