一、反射概述
- 反射允許程序在運行中獲取類的內部信息,例如構造器、成員變量、成員方法等
- 類加載之后,在堆中生成一個Class類的對象(一個類只有一個Class對象),這個對象包含類的完整結構信息
二、入門案例
通過配置文件中的內容生成指定類的對象并調用指定方法
1
2
3
|
// re.properties className=com.javalearn.reflect.Cat methodName=hi |
1
2
3
4
5
6
|
public class Cat { private String name = "招財貓" ; public void hi() { System.out.println( "hi:" + this .name); } } |
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
|
public class ReflectionDemo { public static void main(String[] args) throws Exception { // 1.properties對象加載配置文件 Properties properties = new Properties(); properties.load( new FileInputStream( "src/main/resources/re.properties" )); String className = properties.getProperty( "className" ); String methodName = properties.getProperty( "methodName" ); System.out.println( "類名:" + className); System.out.println( "方法名:" + methodName); // 2.根據類名獲取Class類對象 // 獲取Class對象的三種方式: // 1.類名.class // 2.對象.getClass() // 3.Class.forName(類名) Class cls = Class.forName(className); // 3.生成實例對象 Object o = cls.newInstance(); // 4.獲取方法 Method declaredMethod = cls.getDeclaredMethod(methodName); // 5.方法.invoke(對象) declaredMethod.invoke(o); // 6.反射涉及的其他類 // 6.1Field成員變量 Field name = cls.getDeclaredField( "name" ); name.setAccessible( true ); //private屬性需暴力反射 System.out.println(name.get(o)); // 6.2Constructor構造器 Constructor constructor = cls.getConstructor(); //方法參數類型與構造器的參數類型一致,不寫就是無參構造器 Object o1 = constructor.newInstance(); System.out.println(o1); } } |
三、反射原理圖
Java程序執行的三個階段
- 將Java代碼編譯成字節碼
- 類加載器加載字節碼文件,在堆中生成Class類對象
- 運行階段使用Class對象生成真正的Java類
反射可以做哪些事?
在運行時:
- 判斷任一對象所屬的類
- 構造任一類的對象
- 得到任一類所具有的成員變量和方法
- 調用任一對象的成員變量和方法
- 生成動態代理
四、反射性能測試
反射基本上是解釋執行,性能差
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
|
public class PerformanceDemo { public static void main(String[] args) throws Exception { tradition(); reflect(); } private static void reflect() throws Exception { Class cls = Class.forName( "com.sankuai.yangjin.javalearn.reflect.Cat" ); Object o = cls.newInstance(); Method hi = cls.getMethod( "hi" ); long start = System.currentTimeMillis(); for ( int i = 0 ; i < 10000 ; i++) { hi.invoke(o); } long end = System.currentTimeMillis(); System.out.println( "反射耗時:" + (end - start)); } private static void tradition() { Cat cat = new Cat(); long start = System.currentTimeMillis(); for ( int i = 0 ; i < 10000 ; i++) { cat.hi(); } long end = System.currentTimeMillis(); System.out.println( "傳統耗時:" + (end - start)); } } |
優化方式:
Method、Field、Constructor對象都有setAccessible()方法,可以將參數設置為true,表示在使用反射時取消訪問檢查,效果也就一般般
五、Class類
- Class類也是類,繼承Obejct類
- Class類對象不是new出來的,而是系統創建的
- 對于某個類的Class類對象,在內存中只有一份,因為類只加載一次
- 每個類的實例都知道自己是由哪個Class實例生成,對象.getClass()
- 通過Class對象可以得到類的完整結構
- Class對象是存放在堆的
- 類的字節碼二進制數據(元數據)存放在方法區,包括方法代碼、變量名、方法名、訪問權限等
六、類加載
反射是Java實現動態語言的關鍵,通過反射實現類動態加載
- 靜態加載:編譯時加載相關的類,如果沒有相關的類則報錯,依賴太強
- 動態加載:運行時加載需要的類,如果運行時不用該類,那么即使該類不存在也不報錯
將下面一段代碼通過javac 編譯時,因為并沒有Dog類,所以編譯失敗;但當前同樣沒有Person類,卻不會由于沒有Person類而導致編譯失敗,因為是動態加載,當出現case "2"時才會加載該類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class LoadDemo { public static void main (String[] args) throws Exception { Scanner scanner = new Scanner(System.in); String num = scanner.next(); switch (num) { case "1" : // 靜態加載 Dog dog = new Dog(); break ; case "2" : // 反射,動態加載 Class person = Class.forName( "Person" ); break ; default : } } } |
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!
原文鏈接:https://blog.csdn.net/weufengwangshi_/article/details/119985954