看了很多關於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)框架主要提供以下功能:
- 在運行時判斷任意一個對象所屬的類;
- 在運行時構造任意一個類的對象;
- 在運行時判斷任意一個類所具有的成員變量和方法(通過反射甚至可以調用private方法);
- 在運行時調用任意一個對象的方法
-
Java反射(Reflection)的主要用途
- 工廠模式:Factory類中用反射的話,添加了一個新的類之后,就不需要再修改工廠類Factory了
- 數據庫JDBC中通過Class.forName(Driver).來獲得數據庫連接驅動
- 分析類文件:畢竟能得到類中的方法等等
- 訪問一些不能訪問的變量或屬性:破解別人代碼
-
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)的一些注意事項
- 由於反射會額外消耗一定的系統資源,因此如果不需要動態地創建一個對象,那么就不需要用反射。
- 另外,反射調用方法時可以忽略權限檢查,因此可能會破壞封裝性而導致安全問題。
========================================================
More reading,and english is important.
I'm Hongten
大哥哥大姐姐,覺得有用打賞點哦!多多少少沒關系,一分也是對我的支持和鼓勵。謝謝。
Hongten博客排名在100名以內。粉絲過千。
Hongten出品,必是精品。
E | hongtenzone@foxmail.com B | http://www.cnblogs.com/hongten
========================================================