Java反射原理及應用(一)

一、預先需要掌握的知識(java虛擬機)

java虛擬機的方法區:

java虛擬機有一個運行時數據區,這個數據區又被分爲方法區,堆區和棧區,我們這裏需要了解的主要是方法區。方法區的主要作用是存儲被裝載的類的類型信息,當java虛擬機裝載某個類型的時候,需要類裝載器定位相應的class文件,然後將其讀入到java虛擬機中,緊接着虛擬機提取class中的類型信息,將這些信息存儲到方法區中。這些信息主要包括:

1、這個類型的全限定名

2、這個類型的直接超類的全限定名

3、這個類型是類類型還是接口類型

4、這個類型的訪問修飾符

5、任何直接超接口的全限定名的有序列表

6、該類型的常量池

7、字段信息

8、方法信息

9、除了常量以外的所有類變量

10、一個到class類的引用

等等(讀者可以參考《深入java虛擬機》這本書的敘述)

Class類:

Class類是一個非常重要的java基礎類,每當裝載一個新的類型的時候,java虛擬機都會在java堆中創建一個對應於新類型的Class實例,該實例就代表此類型,通過該Class實例我們就可以訪問該類型的基本信息。上面說到在方法區中會存儲某個被裝載類的類型信息,我們就可以通過Class實例來訪問這些信息。比如,對於上面說到的信息Class中都有對應的方法,如下:

1、getName();這個類型的全限定名

2、getSuperClass();這個類型的直接超類的全限定名

3、isInterface();這個類型是類類型還是接口類型

4、getTypeParamters();這個類型的訪問修飾符

  5、getInterfaces();任何直接超接口的全限定名的有序列表

  6、getFields();字段信息

7、getMethods();方法信息

等等(讀者可以自己參看jdk幫助文檔,得到更多的信息)

二、java反射詳解

反射的概念:所謂的反射就是java語言在運行時擁有一項自觀的能力,反射使您的程序代碼能夠得到裝載到JVM中的類的內部信息,允許您執行程序時纔得到需要類的內部信息,而不是在編寫代碼的時候就必須要知道所需類的內部信息,這使反射成爲構建靈活的應用的主要工具。

反射的常用類和函數:Java反射機制的實現要藉助於4個類:Class,Constructor,Field,Method;其中class代表的是類對象,Constructor-類的構造器對象,Field-類的屬性對象,Method-類的方法對象,通過這四個對象我們可以粗略的看到一個類的各個組成部分。其中最核心的就是Class類,它是實現反射的基礎,它包含的方法我們在第一部分已經進行了基本的闡述。應用反射時我們最關心的一般是一個類的構造器、屬性和方法,下面我們主要介紹Class類中針對這三個元素的方法:

1、得到構造器的方法

Constructor getConstructor(Class[] params) -- 獲得使用特殊的參數類型的公共構造函數,

Constructor[] getConstructors() -- 獲得類的所有公共構造函數

Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定參數類型的構造函數(與接入級別無關)

Constructor[] getDeclaredConstructors() -- 獲得類的所有構造函數(與接入級別無關)

2、獲得字段信息的方法

Field getField(String name) -- 獲得命名的公共字段

Field[] getFields() -- 獲得類的所有公共字段

Field getDeclaredField(String name) -- 獲得類聲明的命名的字段

Field[] getDeclaredFields() -- 獲得類聲明的所有字段
3、獲得方法信息的方法

Method getMethod(String name, Class[] params) -- 使用特定的參數類型,獲得命名的公共方法

Method[] getMethods() -- 獲得類的所有公共方法

Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數類型,獲得類聲明的命名的方法

Method[] getDeclaredMethods() -- 獲得類聲明的所有方法

應用反射的基本步驟:

1、獲得你想操作的類的Class對象;

     方法一:Class c=Class.forName("java.lang.String")

     方法二:對於基本數據類型可以用形如Class c=int.class或Class c=Integer.TYPE的語句

     方法三:Class c=MyClass.class

2、調用Class中的方法得到你想得到的信息集合,如調用getDeclaredFields()方法得到類的所有屬性;

  3、處理第2步中得到的信息,然後進行你想做的實際操作。

  反射實例:

下面我將針對類的構造器、屬性和方法分別舉三個例子,向大家演示一下反射的應用過程。

1、構造器

步驟爲:通過反射機制得到某個類的構造器,然後調用該構造器創建該類的一個實例

import java.lang.reflect.*;
public class ConstructorDemo{
   public ConstructorDemo(){ }
   public ConstructorDemo(int a, int b){
       System.out.println("a="+a+"b="+b);
   }

   public static void main(String args[]){
       try {
           Class cls = Class.forName("ConstructorDemo");
           Class partypes[] = new Class[2]; partypes[0] = Integer.TYPE;  
           partypes[1] = Integer.TYPE;
           Constructor ct= cls.getConstructor(partypes);
           Object arglist[] = new Object[2];
           arglist[0] = new Integer(37);
           arglist[1] = new Integer(47);
           Object retobj = ct.newInstance(arglist);
         } catch (Throwable e) {
           System.err.println(e); }
         }
   }

2、屬性

步驟爲:通過反射機制得到某個類的某個屬性,然後改變對應於這個類的某個實例的該屬性值

import java.lang.reflect.*;
public class FieldDemo1{
  public double d;

  public static void main(String args[]){
  try {
    Class cls = Class.forName("FieldDemo1");
    Field fld = cls.getField("d");
    FieldDemo1 fobj = new FieldDemo1();
    System.out.println("d = " + fobj.d);
    fld.setDouble(fobj, 12.34);
    System.out.println("d = " + fobj.d);
  } catch (Throwable e){
    System.err.println(e); }
  }
}

3、方法

步驟爲:通過反射機制得到某個類的某個方法,然後調用對應於這個類的某個實例的該方法

//通過使用方法的名字調用方法
import java.lang.reflect.*;
public class MethodDemo1{
  public int add(int a, int b){
  return a + b;
  }

  public static void main(String args[]){
    try {
      Class cls = Class.forName("MethodDemo1");
      Class partypes[] = new Class[2];
      partypes[0] = Integer.TYPE;
      partypes[1] = Integer.TYPE;
      Method meth = cls.getMethod("add", partypes);
      MethodDemo1 methobj = new MethodDemo1();
      Object arglist[] = new Object[2];
      arglist[0] = new Integer(37);
      arglist[1] = new Integer(47);
      Object retobj= meth.invoke(methobj, arglist);
      Integer retval = (Integer)retobj;
      System.out.println(retval.intValue());
    } catch (Throwable e) {
      System.err.println(e);
    }
  }
}

三、java反射的應用(Hibernate)

我們在第二部分中對java反射進行了比較系統的闡述,也舉了幾個簡單的實例,下面我們就來討論一下java反射的具體應用。前面我們已經知道,Java反射機制提供了一種動態鏈接程序組件的多功能方法,它允許程序創建和控制任何類的對象(根據安全性限制)之前,無需提前硬編碼目標類。這些特性使得反射特別適用於創建以非常普通的方式與對象協作的庫。例如,反射經常在持續存儲對象爲數據庫、XML或其它外部格式的框架中使用。下面我們就已Hibernate框架爲例像大家闡述一下反射的重要意義。

Hibernate是一個屏蔽了JDBC,實現了ORM的java框架,利用該框架我們可以拋棄掉繁瑣的sql語句而是利用Hibernate中Session類的save()方法直接將某個類的對象存到數據庫中,也就是所涉及到sql語句的那些代碼Hibernate幫我們做了。這時候就出現了一個問題,Hibernate怎樣知道他要存的某個對象都有什麼屬性呢?這些屬性都是什麼類型呢?如此,它在向數據庫中存儲該對象屬性時的sql語句該怎麼構造呢?解決這個問題的利器就是我們的java反射!

下面我們以一個例子來進行闡述,比如我們定義了一個User類,這個User類中有20個屬性和這些屬性的get和set方法,相應的在數據庫中有一個User表,這個User表中對應着20個字段。假設我們從User表中提取了一條記錄,現在需要將這條記錄的20個字段的內容分別賦給一個User對象myUser的20個屬性,而Hibernate框架在編譯的時候並不知道這個User類,他無法直接調用myUser.getXXX或者myUser.setXXX方法,此時就用到了反射,具體處理過程如下:

1、根據查詢條件構造PreparedStament語句,該語句返回20個字段的值;

2、Hibernate通過讀取配置文件得到User類的屬性列表list(是一個String數組)以及這些屬性的類型;

3、創建myUser所屬類的Class對象c;c=myUser.getClass();

4、構造一個for循環,循環的次數爲list列表的長度;

     4.1、讀取list[i]的值,然後構造對應該屬性的set方法;

     4.2、判斷list[i]的類型XXX,調用PreparedStament語句中的getXXX(i),進而得到i出字段的值;

     4.3、將4.2中得到的值作爲4.1中得到的set方法的參數,這樣就完成了一個字段像一個屬性的賦值,如此循環即可;

 

-ref:http://china-jianchen.iteye.com/blog/728774

看到了吧,這就是反射的功勞,如果沒有反射很難想象如果完成同樣的功能會有多麼難!但是反射也有缺點,比如性能比較低、安全性比較複雜等,這裏就不在討論這些東西,感興趣的讀者可以在網上找到答案,有很多的!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章