java 反射(Reflection)-干貨


看了很多關於java 反射的文章,自己把所看到的總結一下。對自己,對他人或多或少有幫助吧。

  • Java Reflection是什么?

首先來看看官方文檔Oracle里面對Reflection的描述:

Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。

簡單的講:

  • 反射機制就是可以把一個類,類的成員(函數,屬性),當成一個對象來操作,希望讀者能理解,也就是說,類,類的成員,我們在運行的時候還可以動態地去操作他們。

再簡單一點的講:

  • 我們可以在運行時獲得程序或程序集中每一個類型的成員和成員的信息。

 

舉個例子:

Java是面向對象語言,我們可以把老虎,獅子等具有相同性質的動物歸類(抽象)為貓科動物,他們具有牙齒,胡須等一些屬性。同時具有吃肉()的動作。

同樣的道理,我們所接觸到的類Class,也可以把他們抽象出來,有類名,成員變量,方法等。

那么既然能夠把類看做是對象,那么java就可以對其進行處理。

 

  • Java反射(Reflection)框架主要提供以下功能:

  1. 在運行時判斷任意一個對象所屬的類;
  2. 在運行時構造任意一個類的對象;
  3. 在運行時判斷任意一個類所具有的成員變量和方法(通過反射甚至可以調用private方法);
  4. 在運行時調用任意一個對象的方法

 

  • Java反射(Reflection)的主要用途

  1. 工廠模式:Factory類中用反射的話,添加了一個新的類之后,就不需要再修改工廠類Factory了
  2. 數據庫JDBC中通過Class.forName(Driver).來獲得數據庫連接驅動
  3. 分析類文件:畢竟能得到類中的方法等等
  4. 訪問一些不能訪問的變量或屬性:破解別人代碼

 

  • Java反射(Reflection)的基本運用

  獲取Class有一下三種方法:

1. 使用Class類的forName靜態方法

 1 //在連接數據庫之前,首先要加載想要連接的數據庫的驅動到JVM(Java虛擬機),   
 2 //這通過java.lang.Class類的靜態方法forName(String  className)實現。   
 3 //例如:   
 4 try{   
 5     //加載MySql的驅動類   
 6     Class.forName("com.mysql.jdbc.Driver") ;   
 7 }catch(ClassNotFoundException e){   
 8     //System.out.println("找不到驅動程序類 ,加載驅動失敗!");   
 9     e.printStackTrace() ;   
10 }   
11 //成功加載后,會將Driver類的實例注冊到DriverManager類中。

 

2. 直接獲取某一個對象的class

1 Class<?> klass = int.class; 
2 Class<?> classInt = Integer.TYPE;

 

3. 調用某個對象的getClass()方法

1 public class Test{
2     public static void main(String[] args) {
3         Demo demo=new Demo();
4         System.out.println(demo.getClass().getName());
5     }
6 }

 

 下面是一個完整的Demo展示Java Reflection的操作:

  1 package com.b510.hongten.test.reflex;
  2 
  3 import java.lang.reflect.Constructor;
  4 import java.lang.reflect.Field;
  5 import java.lang.reflect.InvocationTargetException;
  6 import java.lang.reflect.Method;
  7 import java.lang.reflect.Modifier;
  8 
  9 public class ReflectionDemo {
 10     /**
 11      * 為了看清楚Java反射部分代碼,所有異常我都最后拋出來給虛擬機處理!
 12      * 
 13      * @param args
 14      * @throws ClassNotFoundException
 15      * @throws InstantiationException
 16      * @throws IllegalAccessException
 17      * @throws InvocationTargetException
 18      * @throws IllegalArgumentException
 19      * @throws NoSuchFieldException
 20      * @throws SecurityException
 21      * @throws NoSuchMethodException
 22      */
 23     public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchFieldException, NoSuchMethodException {
 24         // Demo1. 通過Java反射機制得到類的包名和類名
 25         Demo1();
 26         System.out.println("===============================================");
 27 
 28         // Demo2. 驗證所有的類都是Class類的實例對象
 29         Demo2();
 30         System.out.println("===============================================");
 31 
 32         // Demo3. 通過Java反射機制,用Class 創建類對象[這也就是反射存在的意義所在],無參構造
 33         Demo3();
 34         System.out.println("===============================================");
 35 
 36         // Demo4: 通過Java反射機制得到一個類的構造函數,並實現構造帶參實例對象
 37         Demo4();
 38         System.out.println("===============================================");
 39 
 40         // Demo5: 通過Java反射機制操作成員變量, set 和 get
 41         Demo5();
 42         System.out.println("===============================================");
 43 
 44         // Demo6: 通過Java反射機制得到類的一些屬性: 繼承的接口,父類,函數信息,成員信息,類型等
 45         Demo6();
 46         System.out.println("===============================================");
 47 
 48         // Demo7: 通過Java反射機制調用類中方法
 49         Demo7();
 50         System.out.println("===============================================");
 51 
 52         // Demo8: 通過Java反射機制獲得類加載器
 53         Demo8();
 54         System.out.println("===============================================");
 55 
 56     }
 57 
 58     /**
 59      * Demo1: 通過Java反射機制得到類的包名和類名
 60      */
 61     public static void Demo1() {
 62         Person person = new Person();
 63         System.out.println("Demo1: 包名: " + person.getClass().getPackage().getName() + "," + "完整類名: " + person.getClass().getName());
 64         
 65         /**
 66         運行結果:
 67         Demo1: 包名: com.b510.hongten.test.reflex,完整類名: com.b510.hongten.test.reflex.Person
 68         */
 69     }
 70 
 71     /**
 72      * Demo2: 驗證所有的類都是Class類的實例對象 
 73      * 
 74      * @throws ClassNotFoundException
 75      */
 76     public static void Demo2() throws ClassNotFoundException {
 77         // 定義兩個類型都未知的Class , 設置初值為null, 看看如何給它們賦值成Person類
 78         Class<?> class1 = null;
 79         Class<?> class2 = null;
 80 
 81         // 寫法1, 可能拋出 ClassNotFoundException [多用這個寫法]
 82         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
 83         System.out.println("Demo2:(寫法1) 包名: " + class1.getPackage().getName() + "," + "完整類名: " + class1.getName());
 84 
 85         /**
 86         運行結果:
 87         Demo2:(寫法1) 包名: com.b510.hongten.test.reflex,完整類名: com.b510.hongten.test.reflex.Person
 88         */
 89         
 90         // 寫法2
 91         class2 = Person.class;
 92         System.out.println("Demo2:(寫法2) 包名: " + class2.getPackage().getName() + "," + "完整類名: " + class2.getName());
 93         
 94         /**
 95         運行結果:
 96         Demo2:(寫法2) 包名: com.b510.hongten.test.reflex,完整類名: com.b510.hongten.test.reflex.Person
 97         */
 98     }
 99 
100     /**
101      * Demo3: 通過Java反射機制,用Class 創建類對象[這也就是反射存在的意義所在]
102      * 
103      * @throws ClassNotFoundException
104      * @throws IllegalAccessException
105      * @throws InstantiationException
106      */
107     public static void Demo3() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
108         Class<?> class1 = null;
109         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
110         // 由於這里不能帶參數,所以你要實例化的這個類Person,一定要有無參構造函數哈~
111         Person person = (Person) class1.newInstance();
112         person.setAge(20);
113         person.setName("Hongten");
114         System.out.println("Demo3: " + person.getName() + " : " + person.getAge());
115         
116         /**
117         運行結果:
118         Demo3: Hongten : 20
119         */
120     }
121 
122     /**
123      * Demo4: 通過Java反射機制得到一個類的構造函數,並實現創建帶參實例對象
124      * 
125      * @throws ClassNotFoundException
126      * @throws InvocationTargetException
127      * @throws IllegalAccessException
128      * @throws InstantiationException
129      * @throws IllegalArgumentException
130      */
131     public static void Demo4() throws ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
132         Class<?> class1 = null;
133         Person person1 = null;
134         Person person2 = null;
135 
136         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
137         // 得到一系列構造函數集合
138         Constructor<?>[] constructors = class1.getConstructors();
139 
140         person1 = (Person) constructors[0].newInstance();
141         person1.setAge(30);
142         person1.setName("Hongten");
143 
144         person2 = (Person) constructors[1].newInstance(20, "Hongten");
145 
146         System.out.println("Demo4: " + person1.getName() + " : " + person1.getAge() + "  ,   " + person2.getName() + " : " + person2.getAge());
147 
148         /**
149         運行結果:
150         Demo4: Hongten : 30  ,   Hongten : 20
151         */
152     }
153 
154     /**
155      * Demo5: 通過Java反射機制操作成員變量, set 和 get
156      * 
157      * @throws IllegalAccessException
158      * @throws IllegalArgumentException
159      * @throws NoSuchFieldException
160      * @throws SecurityException
161      * @throws InstantiationException
162      * @throws ClassNotFoundException
163      */
164     public static void Demo5() throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException, InstantiationException, ClassNotFoundException {
165         Class<?> class1 = null;
166         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
167         Object obj = class1.newInstance();
168 
169         Field personNameField = class1.getDeclaredField("name");
170         personNameField.setAccessible(true);
171         personNameField.set(obj, "HONGTEN");
172 
173         System.out.println("Demo5: 修改屬性之后得到屬性變量的值:" + personNameField.get(obj));
174 
175         /**
176         運行結果:
177         Demo5: 修改屬性之后得到屬性變量的值:HONGTEN
178         */
179     }
180 
181     /**
182      * Demo6: 通過Java反射機制得到類的一些屬性: 繼承的接口,父類,函數信息,成員信息,類型等
183      * 
184      * @throws ClassNotFoundException
185      */
186     public static void Demo6() throws ClassNotFoundException {
187         Class<?> class1 = null;
188         class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan");
189 
190         // 取得父類名稱
191         Class<?> superClass = class1.getSuperclass();
192         System.out.println("Demo6:  SuperMan類的父類名: " + superClass.getName());
193 
194         /**
195         運行結果:
196         Demo6:  SuperMan類的父類名: com.b510.hongten.test.reflex.Person
197         */
198         
199         System.out.println("===============================================");
200 
201         Field[] fields = class1.getDeclaredFields();
202         for (int i = 0; i < fields.length; i++) {
203             System.out.println("類中的成員: " + fields[i]);
204         }
205         /**
206         運行結果:
207         類中的成員: private boolean com.b510.hongten.test.reflex.SuperMan.BlueBriefs
208         */
209         
210         System.out.println("===============================================");
211 
212         // 取得類方法
213         Method[] methods = class1.getDeclaredMethods();
214         for (int i = 0; i < methods.length; i++) {
215             System.out.println("Demo6,取得SuperMan類的方法:");
216             System.out.println("函數名:" + methods[i].getName());
217             System.out.println("函數返回類型:" + methods[i].getReturnType());
218             System.out.println("函數訪問修飾符:" + Modifier.toString(methods[i].getModifiers()));
219             System.out.println("函數代碼寫法: " + methods[i]);
220         }
221 
222         /**
223         運行結果:
224         Demo6,取得SuperMan類的方法:
225         函數名:isBlueBriefs
226         函數返回類型:boolean
227         函數訪問修飾符:public
228         函數代碼寫法: public boolean com.b510.hongten.test.reflex.SuperMan.isBlueBriefs()
229         Demo6,取得SuperMan類的方法:
230         函數名:setBlueBriefs
231         函數返回類型:void
232         函數訪問修飾符:public
233         函數代碼寫法: public void com.b510.hongten.test.reflex.SuperMan.setBlueBriefs(boolean)
234         Demo6,取得SuperMan類的方法:
235         函數名:fly
236         函數返回類型:void
237         函數訪問修飾符:public
238         函數代碼寫法: public void com.b510.hongten.test.reflex.SuperMan.fly()
239         Demo6,取得SuperMan類的方法:
240         函數名:walk
241         函數返回類型:void
242         函數訪問修飾符:public
243         函數代碼寫法: public void com.b510.hongten.test.reflex.SuperMan.walk(int)
244         */
245         
246         System.out.println("===============================================");
247 
248         // 取得類實現的接口,因為接口類也屬於Class,所以得到接口中的方法也是一樣的方法得到哈
249         Class<?> interfaces[] = class1.getInterfaces();
250         for (int i = 0; i < interfaces.length; i++) {
251             System.out.println("實現的接口類名: " + interfaces[i].getName());
252         }
253         
254         /**
255         運行結果:
256         實現的接口類名: com.b510.hongten.test.reflex.ActionInterface
257         */
258 
259     }
260 
261     /**
262      * Demo7: 通過Java反射機制調用類方法
263      * 
264      * @throws ClassNotFoundException
265      * @throws NoSuchMethodException
266      * @throws SecurityException
267      * @throws InvocationTargetException
268      * @throws IllegalAccessException
269      * @throws IllegalArgumentException
270      * @throws InstantiationException
271      */
272     public static void Demo7() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
273         Class<?> class1 = null;
274         class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan");
275 
276         System.out.println("Demo7: \n調用無參方法fly()");
277         Method method = class1.getMethod("fly");
278         method.invoke(class1.newInstance());
279 
280         System.out.println("調用有參方法walk(int m)");
281         method = class1.getMethod("walk", int.class);
282         method.invoke(class1.newInstance(), 100);
283         
284         /**
285         運行結果:
286         Demo7: 
287         調用無參方法fly()
288         fly method....
289         調用有參方法walk(int m)
290         fly in 100 m
291         */
292         
293     }
294 
295     /**
296      * Demo8: 通過Java反射機制得到類加載器信息
297      * 
298      * 在java中有三種類類加載器。[這段資料網上截取]
299      * 
300      * 1)Bootstrap ClassLoader 此加載器采用c++編寫,一般開發中很少見。
301      * 
302      * 2)Extension ClassLoader 用來進行擴展類的加載,一般對應的是jre\lib\ext目錄中的類
303      * 
304      * 3)AppClassLoader 加載classpath指定的類,是最常用的加載器。同時也是java中默認的加載器。
305      * 
306      * @throws ClassNotFoundException
307      */
308     public static void Demo8() throws ClassNotFoundException {
309         Class<?> class1 = null;
310         class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan");
311         String nameString = class1.getClassLoader().getClass().getName();
312 
313         System.out.println("Demo8: 類加載器類名: " + nameString);
314         
315         /**
316         運行結果:
317         Demo8: 類加載器類名: sun.misc.Launcher$AppClassLoader
318         */
319     }
320 
321 }
322 
323 
324 class Person {
325     private int age;
326     private String name;
327 
328     public Person() {
329 
330     }
331 
332     public Person(int age, String name) {
333         this.age = age;
334         this.name = name;
335     }
336 
337     public int getAge() {
338         return age;
339     }
340 
341     public void setAge(int age) {
342         this.age = age;
343     }
344 
345     public String getName() {
346         return name;
347     }
348 
349     public void setName(String name) {
350         this.name = name;
351     }
352 }
353 
354 class SuperMan extends Person implements ActionInterface {
355     private boolean BlueBriefs;
356 
357     public void fly() {
358         System.out.println("fly method....");
359     }
360 
361     public boolean isBlueBriefs() {
362         return BlueBriefs;
363     }
364 
365     public void setBlueBriefs(boolean blueBriefs) {
366         BlueBriefs = blueBriefs;
367     }
368 
369     public void walk(int m) {
370         System.out.println("fly in " + m + " m");
371     }
372 }
373 
374 interface ActionInterface {
375     public void walk(int m);
376 }

 

  •  說說工廠模式和Java 反射(Reflection)機制

 如果在工廠模式下面,我們不使用Java 反射(Reflection)機制,會是什么樣子呢?

 1 package com.b510.hongten.test.reflex;
 2 
 3 /**
 4  * @author hongten
 5  * @created Apr 18, 2018
 6  */
 7 public class FactoryTest {
 8 
 9     public static void main(String[] args) {
10         Cats cats = FactoryTest.getInstance("Tiger");
11         cats.eatMeat();
12 
13         /*
14          * The Result : The tiger eat meat.
15          */
16     }
17 
18     public static Cats getInstance(String name) {
19         Cats cats = null;
20         if ("Tiger".equals(name)) {
21             cats = new Tiger();
22         }
23         if ("Lion".equals(name)) {
24             cats = new Lion();
25         }
26         return cats;
27     }
28 }
29 
30 interface Cats {
31     public abstract void eatMeat();
32 }
33 
34 class Tiger implements Cats {
35 
36     public void eatMeat() {
37         System.out.println("The tiger eat meat.");
38     }
39 
40 }
41 
42 class Lion implements Cats {
43 
44     public void eatMeat() {
45         System.out.println("The lion eat meat.");
46     }
47 
48 }

我們會發現:

當我們在添加一個子類(Puma-美洲獅)的時候,就需要修改工廠類了。如果我們添加太多的子類的時候,改的就會很多。

我們對程序進行修改,加入反射機制。

 1 package com.b510.hongten.test.reflex;
 2 
 3 /**
 4  * @author hongten
 5  * @created Apr 18, 2018
 6  */
 7 public class FactoryTest {
 8 
 9     public static void main(String[] args) {
10         //Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Lion");
11         Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Tiger");
12         cats.eatMeat();
13 
14         /*
15          * The Result : The tiger eat meat.
16          */
17     }
18 
19     public static Cats getInstance(String name) {
20         Cats cats = null;
21         try {
22             try {
23                 //use Class.forName() with java reflection
24                 cats = (Cats) Class.forName(name).newInstance();
25             } catch (InstantiationException e) {
26                 e.printStackTrace();
27             } catch (IllegalAccessException e) {
28                 e.printStackTrace();
29             }
30         } catch (ClassNotFoundException e) {
31             e.printStackTrace();
32         }
33         return cats;
34     }
35 }
36 
37 interface Cats {
38     public abstract void eatMeat();
39 }
40 
41 class Tiger implements Cats {
42 
43     public void eatMeat() {
44         System.out.println("The tiger eat meat.");
45     }
46 
47 }
48 
49 class Lion implements Cats {
50 
51     public void eatMeat() {
52         System.out.println("The lion eat meat.");
53     }
54 
55 }

我們會發現利用了Java Reflection以后,現在就算我們添加任意多個子類的時候,工廠類就不需要修改。我們只需要傳遞工廠類的實現類的名稱即可。

然而,在這樣的情況下面,如果我們需要運行Lion實例的時候,我們還需要去修改java代碼。

1 Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Lion");

上面的代碼雖然可以通過反射取得接口的實例,但是需要傳入完整的包和類名。而且用戶也無法知道一個接口有多少個可以使用的子類,所以我們通過屬性文件的形式配置所需要的子類。

能不能不需要修改java代碼,同樣可以完成相同的操作呢?是可以的。

我們可以讓工廠模式結合屬性文件(如:*.properties, *.xml文件)

 屬性文件:/reflex/cats.properties

1 lion=com.b510.hongten.test.reflex.Lion
2 tiger=com.b510.hongten.test.reflex.Tiger

測試文件:/reflex/FactoryTest.java

 1 package com.b510.hongten.test.reflex;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.util.Properties;
 9 
10 /**
11  * @author hongten
12  * @created Apr 18, 2018
13  */
14 public class FactoryTest {
15 
16     public static void main(String[] args) {
17         Cats cats;
18         try {
19             cats = FactoryTest.getInstance(getPro().getProperty("tiger"));
20             if (cats != null) {
21                 cats.eatMeat();
22             }
23         } catch (FileNotFoundException e) {
24             e.printStackTrace();
25         } catch (IOException e) {
26             e.printStackTrace();
27         }
28 
29         /*
30          * The Result : The tiger eat meat.
31          */
32     }
33 
34     public static Cats getInstance(String name) {
35         Cats cats = null;
36         try {
37             try {
38                 // use Class.forName() with java reflection
39                 cats = (Cats) Class.forName(name).newInstance();
40             } catch (InstantiationException e) {
41                 e.printStackTrace();
42             } catch (IllegalAccessException e) {
43                 e.printStackTrace();
44             }
45         } catch (ClassNotFoundException e) {
46             e.printStackTrace();
47         }
48         return cats;
49     }
50 
51     public static Properties getPro() throws FileNotFoundException, IOException {
52         Properties pro = new Properties();
53         File f = new File("cats.properties");
54         if (f.exists()) {
55             pro.load(new FileInputStream(f));
56         } else {
57             pro.setProperty("lion", "com.b510.hongten.test.reflex.Lion");
58             pro.setProperty("tiger", "com.b510.hongten.test.reflex.Tiger");
59             pro.store(new FileOutputStream(f), "FRUIT CLASS");
60         }
61         return pro;
62     }
63 }
64 
65 interface Cats {
66     public abstract void eatMeat();
67 }
68 
69 class Tiger implements Cats {
70 
71     public void eatMeat() {
72         System.out.println("The tiger eat meat.");
73     }
74 
75 }
76 
77 class Lion implements Cats {
78 
79     public void eatMeat() {
80         System.out.println("The lion eat meat.");
81     }
82 
83 }

作為開發者,我們只需要配置cats.properties屬性文件即可。極大的提高了程序的擴展性能。

  • Java反射(Reflection)的一些注意事項

  1. 由於反射會額外消耗一定的系統資源,因此如果不需要動態地創建一個對象,那么就不需要用反射。
  2. 另外,反射調用方法時可以忽略權限檢查,因此可能會破壞封裝性而導致安全問題。

 

========================================================

More reading,and english is important.

I'm Hongten

 

大哥哥大姐姐,覺得有用打賞點哦!多多少少沒關系,一分也是對我的支持和鼓勵。謝謝。
Hongten博客排名在100名以內。粉絲過千。
Hongten出品,必是精品。

E | hongtenzone@foxmail.com  B | http://www.cnblogs.com/hongten

========================================================


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM