反射非常強大和有用,現在市面上絕大部分框架(spring、mybatis、rocketmq等等)中都有反射的影子,反射機制在框架設計中占有舉足輕重的作用。
所以,在你Java進階的道路上,你需要掌握好反射。
怎么才能學好反射,我們需要弄懂以下幾個問題:
1.反射是什么?
2.反射有什么用?
3.反射的實現原理?
4.怎么用反射?
下面我就針對以上的疑問,一一來講解。
反射是什么?
反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。
反射是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法,對於任意一個對象,都能夠調用它的任意方法和屬性,這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。
一句話總結:反射就是在運行時才知道要操作的類是什么,並且可以在運行時獲取類的完整構造,並調用對應的方法。
為什么要用反射?
Java Reflection功能非常強大,並且非常有用,比如:
- 獲取任意類的名稱、package信息、所有屬性、方法、注解、類型、類加載器等
- 獲取任意對象的屬性,並且能改變對象的屬性
- 調用任意對象的方法
- 判斷任意一個對象所屬的類
- 實例化任意一個類的對象
- 通過反射我們可以實現動態裝配,降低代碼的耦合度,動態代理等。
怎么使用反射?
一般情況下我們通過反射創建類對象主要有兩種方式:
- 通過 Class 對象的 newInstance() 方法
- 通過 Constructor 對象的 newInstance() 方法
第一種:通過 Class 對象的 newInstance() 方法。
- Class clz = Class.forName("com.mikechen.reflection.JiaGou");
- JiaGou jg= (JiaGou)clz.newInstance();
第二種:通過 Constructor 對象的 newInstance() 方法
- Class clz = Class.forName("com.mikechen.reflection.JiaGou");
- Constructor constructor = clz.getConstructor();
- JiaGou jg= (JiaGou)constructor.newInstance();
通過 Constructor 對象創建類對象可以選擇特定構造方法,而通過 Class 對象則只能使用默認的無參數構造方法,下面的代碼就調用了一個有參數的構造方法進行了類對象的初始化。
- Class clz = Class.forName("com.mikechen.reflection.JiaGou");
- Constructor constructor = clz.getConstructor(String.class);
- JiaGou jg= (JiaGou)constructor.newInstance("mikechen的互聯網架構");
接下來我們就可以通過具體的API調用獲取到詳細的屬性或者方法等詳細了。
1、獲取類的成員變量的信息
- //mikechen的互聯網架構
- Field[] fields = cls.getDeclaredFields();
更加詳細成員變量獲取參考如下:
2、獲得類方法
- //mikechen的互聯網架構
- Method[] methods = cls.getDeclaredMethods();
更加詳細方法獲取參考如下:
3、獲得構造函數
- //mikechen的互聯網架構
- Constructor[] constructors = cls.getDeclaredConstructors();
更加詳細構造函數獲取參考如下:
這樣通過反射就可以做在運行時獲取類的完整構造,並獲得類信息了。
通過以上一個小案例了解了反射的使用,但如果你想對反射掌握得更好,還需深入理解反射背后的底層實現原理。
反射工作原理?
調用反射的總體流程如下:
1、當我們編寫完一個Java項目之后,每個java文件都會被編譯成一個.class文件。
2、這些class文件在程序運行時會被ClassLoader加載到JVM中,當一個類被加載以后,JVM就會在內存中自動產生一個Class對象。
3、通過Class對象獲取Field/Method/Construcor
我們一般平時是通過new的形式創建對象實際上就是通過這些Class來創建的,只不過這個class文件是編譯的時候就生成的,程序相當於寫死了給jvm去跑。
反射是什么呢?當我們的程序在運行時,需要動態的加載一些類這些類可能之前用不到所以不用加載到jvm,而是在運行時根據需要才加載。
原來使用new的時候,需要明確的指定類名,這個時候屬於硬編碼實現,而在使用反射的時候,可以只傳入類名參數,就可以生成對象,降低了耦合性,使得程序更具靈活性。
反射的應用場景
舉個例子我們的項目底層數據庫有時是用mysql,有時用oracle,需要動態地根據實際情況加載驅動類,這個時候反射就有用了,假設 com.mikechen.java.myqlConnection,com.mikechen.java.oracleConnection這兩個類我們要用。
這時候我們在使用 JDBC 連接數據庫時使用 Class.forName()通過反射加載數據庫的驅動程序,如果是mysql則傳入mysql的驅動類,而如果是oracle則傳入的參數就變成另一個了。
Spring 框架的 IOC(動態加載管理 Bean),Spring通過配置文件配置各種各樣的bean,你需要用到哪些bean就配哪些,spring容器就會根據你的需求去動態加載,你的程序就能健壯地運行。
還有Spring AOP(動態代理)功能都和反射有關系。
除此之外還有很多框架:mybatis、dubbo、rocketmq等等都會用到反射機制。