1. 程式人生 > >Java 反射詳解

Java 反射詳解

什麽 tco type 性能 bob 參數 bject 今天 erl

  反射反射,程序員的快樂,今天你快樂了嗎?如果你不快樂,沒關系,接下來讓你快樂起來!

一、什麽是反射?
  通過百度百科我們可以知道,Java反射就是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;並且能改變它的屬性。而這也是Java被視為動態(或準動態,為啥要說是準動態,因為一般而言的動態語言定義是程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。)語言的一個關鍵性質。

二、反射能做什麽?
  我們知道反射機制允許程序在運行時取得任何一個已知名稱的class的內部信息,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,並可於運行時改變fields內容或調用methods。那麽我們便可以更靈活的編寫代碼,代碼可以再運行時裝配,無需在組件之間進行源代碼鏈接,降低代碼的耦合度;還有動態代理的實現等等;但是需要註意的時反射使用不當會成本很高!

三、反射的具體實現

  下面是一個基本的類 Person

技術分享
 1 package com.ys.reflex;
 2 public class Person {
 3     //私有屬性
 4     private String name = "Tom";
 5     //公有屬性
 6     public int age = 18;
 7     //構造方法
 8     public Person() {    
 9     }
10     //私有方法
11     private void say(){
12         System.out.println("private say()...");
13     }
14     //公有方法
15     public void work(){
16         System.out.println("public work()...");
17     }
18 }
技術分享

①、得到 Class 的三種方式

技術分享
 1 //1、通過對象調用 getClass() 方法來獲取,通常應用在:比如你傳過來一個 Object
 2 //  類型的對象,而我不知道你具體是什麽類,用這種方法
 3   Person p1 = new Person();
 4   Class c1 = p1.getClass();
 5         
 6 //2、直接通過 類名.class 的方式得到,該方法最為安全可靠,程序性能更高
 7 //  這說明任何一個類都有一個隱含的靜態成員變量 class
 8   Class c2 = Person.class;
 9         
10 //3、通過 Class 對象的 forName() 靜態方法來獲取,用的最多,
11 //   但可能拋出 ClassNotFoundException 異常
12   Class c3 = Class.forName("com.ys.reflex.Person");
技術分享

需要註意的是:一個類在 JVM 中只會有一個 Class 實例,即我們對上面獲取的 c1,c2,c3進行 equals 比較,發現都是true

②、通過 Class 類獲取成員變量、成員方法、接口、超類、構造方法等

查閱 API 可以看到 Class 有很多方法:

  getName():獲得類的完整名字。
  getFields():獲得類的public類型的屬性。
  getDeclaredFields():獲得類的所有屬性。包括private 聲明的和繼承類
  getMethods():獲得類的public類型的方法。
  getDeclaredMethods():獲得類的所有方法。包括private 聲明的和繼承類
  getMethod(String name, Class[] parameterTypes):獲得類的特定方法,name參數指定方法的名字,parameterTypes 參數指定方法的參數類型。
  getConstructors():獲得類的public類型的構造方法。
  getConstructor(Class[] parameterTypes):獲得類的特定構造方法,parameterTypes 參數指定構造方法的參數類型。
  newInstance():通過類的不帶參數的構造方法創建這個類的一個對象。

我們通過一個例子來綜合演示上面的方法:

技術分享
 1 //獲得類完整的名字
 2 String className = c2.getName();
 3 System.out.println(className);//輸出com.ys.reflex.Person
 4         
 5 //獲得類的public類型的屬性。
 6 Field[] fields = c2.getFields();
 7 for(Field field : fields){
 8    System.out.println(field.getName());//age
 9 }
10         
11 //獲得類的所有屬性。包括私有的和繼承類的
12 Field [] allFields = c2.getDeclaredFields();
13 for(Field field : allFields){
14     System.out.println(field.getName());//name    age
15 }
16         
17 //獲得類的public類型的方法。這裏包括 Object 類的一些方法
18 Method [] methods = c2.getMethods();
19 for(Method method : methods){
20     System.out.println(method.getName());//work waid equls toString hashCode等
21 }
22         
23 //獲得類的所有方法。
24 Method [] allMethods = c2.getDeclaredMethods();
25 for(Method method : allMethods){
26     System.out.println(method.getName());//work say
27 }
28         
29 //獲得指定的屬性
30 Field f1 = c2.getField("age");
31 System.out.println(f1);
32 //獲得指定的私有屬性
33 Field f2 = c2.getDeclaredField("name");
34 //啟用和禁用訪問安全檢查的開關,值為 true,則表示反射的對象在使用時應該取消 java 語言的訪問檢查;反之不取消
35 f2.setAccessible(true);
36 System.out.println(f2);
37                 
38 //創建這個類的一個對象
39 Object p2 =  c2.newInstance();
40 //將 p2 對象的  f2 屬性賦值為 Bob,f2 屬性即為 私有屬性 name
41 f2.set(p2,"Bob");
42 //使用反射機制可以打破封裝性,導致了java對象的屬性不安全。 
43 System.out.println(f2.get(p2)); //Bob
44         
45 //獲取構造方法
46 Constructor [] constructors = c2.getConstructors();
47 for(Constructor constructor : constructors){
48     System.out.println(constructor.toString());//public com.ys.reflex.Person()
49 }
技術分享

四、反射總結

靈活使用反射能讓我們代碼更加靈活,這裏比如JDBC原生代碼註冊驅動,hibernate 的實體類,Spring 的 AOP等等都有反射的實現。但是凡事都有兩面性,反射也會讓增加系統的性能,復雜性等,合理使用才是真!

Java 反射詳解