java 反射機制

概述:
1.反射機制的本質
反射機制就是對I/O流的封裝版,讀取硬盤上的class文件,同時也是SUN公司對客戶做出讓步的選擇。
反射機制通過將硬盤上的class文件加載到內存,讓開發人員瞭解當前的【陌生對象】
     
2.class文件有什麼作用:
    class文件是【解釋文件】。JVM可以通過【解釋文件】瞭解當前【實例對象】的特徵和行爲

3.class文件在硬盤上的位置:
     1.java prorject 中class文件 在 bin文件下的
     2.web  project  中class文件 在 tomcat---》webapps--->當前工程--->WEB-info-->bin--->classess
4.Class對象的含義
              我們知道,類是對現實的某種事物的抽象定義,而Class是對於類的抽象定義。


 一:獲得某個類的Class引用這個Class是類在內存中的class文件
               通過String指定要獲取那個類的Class.注意必須是完整的類名(也就是包名+類名格式)
        獲取自定義類的相關信息.這個自定義的類必須要在當前項目中纔可以

      
           注意:
 Class.forName() 是反射機制【加載】硬盤上的class文件,會創建class文件對象.
 實例對象.getClass() 或者 類名.class 是反射機制【定位】內存中已經存在的class文件

 二:通過相關Class的引用調用相關方法,得到其方法,屬性,構造方法的相關信息:

         Class類中的重要API

     
 ClassLoadergetClassLoader()                                                                         
          返回該類的類加載器。

  1. 獲取Field(屬性)----->包含屬性的修飾符,類型及名稱 如:[private int age];
    FieldgetDeclaredField(String name)
              返回一個 Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段。
    Field[]getDeclaredFields()
              返回 Field 對象的一個數組,這些對象反映此 Class 對象所表示的類或接口所聲明的所有字段
  2. 獲取Method(普通方法)----->包含方法的修飾符,返回值類型,方法名稱及拋出的異常 如:[public string getName()];


    Method

    getDeclaredMethod(String name, Class<?>... parameterTypes)

              返回一個 Method 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方

    Method[]

    getDeclaredMethods()

              返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或接口聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。



  3. 獲取Constructor(構造方法)----->包含構造方法的修飾符,方法名稱及拋出的異常 如:[public Student()];
 Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes)
          返回一個 Constructor 對象,該對象反映此 Class 對象所表示的類或接口的指定構造方法。
 Constructor<?>[]getDeclaredConstructors()
          返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的所有構造方法。



  例如:(claz是相關類的字節碼對象的引用)
                                   
            Constructor[] cons = claz.getConstructors();//
claz.getConstructors()獲取的是類中的所有的公開的構造方法

             claz.getDeclaredConstructors()//獲取類中聲聲明的構造方法的數組,包括公開的,保護的,缺省的,私有的構造方法     


三:不同的操作“對象”,使用不同的API完成想做的功能

          在java中把構造方法封裝成Constructor類,屬性封裝成Field類,方法封裝成Method類
而他們是繼承Class實現AccessibleObject接口(在java.lang.reflect包下)。而這個接口中定義的以下的方法


setAccessible(boolean flag)將此對象的 accessible 標誌設置爲指示的布爾值。
這個方法三個類中都實現了,該方法是設置是否能夠訪問privat 修飾的屬性,方法,構造方法。

                                                                   繼承圖
               

四:反射的代碼示例:

以下示例一代碼是模擬Spring框架中將前臺數據轉換爲實體類的小Demo。 前臺數據的key必須保證和實體類中的屬性名稱絕對一致

public class ReflectUtil {
          
        /**
         *   過程:  前臺數據------>後臺實體類對象
         * 功能:將【添加頁面】發送的請求保存到對應的實體類對象中
         * @param req  包含本次請求包含的請求參數
         * @param classObj  與當前【添加頁面】對應的【實體類文件】
         * @return
         */
        public static Object parseRequest(HttpServletRequest req,Class classObj)throws Exception{
                        //1. 根據獲得【class文件】,創建一個【實例對象】
                           Object obj= classObj.newInstance();
                        //2. 獲得本次請求,涉及的所有的【請求參數名稱】
                           Enumeration paramArray= req.getParameterNames();
                        // 3.  循環遍歷【枚舉】
                           while(paramArray.hasMoreElements()){
                                     // 3.1 每一次循環,從【枚舉】中獲得一個【請求參數名稱】   sid
                                     String paramName= (String) paramArray.nextElement();
                                     //3.2 到【class文件】尋找與當前【請求參數名稱】相同的【同名屬性對象】
                                     //Field fieldObj=classObj.getDeclaredField(paramName);
                                     //表單域中有可能存在非實體類中的屬性,所以採用以下方式
                                     Field fieldObj =null;
                                     try{
                                         fieldObj=classObj.getDeclaredField(paramName);
                                     }catch(NoSuchFieldException ex){
                                         System.out.println("屬性"+paramName+"在當前類 "+classObj.getName()+"不存在");
                                         continue;
                                     }
                                     fieldObj.setAccessible(true);
                                     // 3.3 讀取【請求參數】包含的【內容】
                                     String value = req.getParameter(paramName);
                                     //3.4 讀取【同名屬性對象】的【數據類型名稱】
                                     String typeName=fieldObj.getType().getName();//[int,double,String ,java.util.Date,boolean]
                                      //3.5 根據【同名屬性對象】的【數據類型名稱】對【請求參數】包含的【內容】進行【類型轉換】
                                     Object data=convertType(typeName,value);
                                     // 3.6 將轉換後的數據賦值給【同名屬性對象】
                                     fieldObj.set(obj, data);//通知JVM,強制爲當前【實例對象】中指定的【屬性】進行賦值。
                           }
                           //4.  返回賦值好的【實例對象】
                           return obj;
        }

    實例二:模擬數據庫數據轉換爲實體類
       
 /**
         *   過程: 數據庫數據------>後臺實體類對象
         * 功能:resultSet -----> List
         * @param rs
         * @param xmlPath
         * @return
         */
        public static List convert(ResultSet rs,String xmlPath)throws Exception{
                          //0.局部變量
                           List list = new ArrayList();
                          // 1.將硬盤上【實體類映射文件】加載到內存
                          InputStream in = new FileInputStream(xmlPath);
                          SAXReader saxObj = new SAXReader();
                          Document xmlObj = saxObj.read(in);
                          //2.獲得【臨時表】包含【內部結構】
                          ResultSetMetaData rsmd= rs.getMetaData();
                          //3.從【實體類映射文件】獲得與當前【臨時表】對應【實體類文件】
                          String xPath="//@class";
                          Attribute attrObj= (Attribute) xmlObj.selectSingleNode(xPath);
                          String classPath =attrObj.getValue();
                          Class classObj=Class.forName(classPath);
                          // 4.循環【臨時表數據行】
                          while(rs.next()){
                                  //4.1,每次循環時,生成一個【實例對象】
                                   Object obj = classObj.newInstance();
                                   //4.2  循環【當前數據行】中的字段信息
                                   for(int col=1;col<=rsmd.getColumnCount();col++){
                                           //4.2.1  每次循環,獲得【當前數據行】中一個【字段名稱】
                                           String colName = rsmd.getColumnName(col); //DEPTNO
                                           //4.2.2  到  【實體類映射文件】尋找與【字段名稱】對應【屬性名】
                                           xPath="//property[@colName='"+colName+"']";
                                           Element property= (Element) xmlObj.selectSingleNode(xPath);
                                           String fieldName= property.attributeValue("name"); //departId
                                           //4.2.3  到  【實體類文件】尋找對應的【屬性對象】   
                                           Field fieldObj=classObj.getDeclaredField(fieldName);// private int departId;
                                           fieldObj.setAccessible(true);
                                           //4.2.4  讀取【當前數據行】,這個【字段名稱】關聯的數據
                                           String value = rs.getString(colName);
                                           // 4.2.5  根據【屬性對象】的數據類型名稱,對取得字段內容進行類型轉換
                                           String typeName=fieldObj.getType().getName();
                                           Object data=convertType(typeName,value);
                                           // 4.2.6  將轉換後的數據添加到【屬性對象】     屬性對象.set(【實例對象】,data)
                                           fieldObj.set(obj, data);
                                   }
                                   //4.3   將賦值好的 【實例對象】保存到list
                                   list.add(obj);
                          }
                          //5.將list返回  
                          return list;
        }
          //公用的判斷數據類型並轉換的方法
          private static Object convertType(String typeName,String value)throws Exception{
                  Object data =null;//保存類型轉換後的數據
             if("int".equals(typeName)){
                 data = Integer.valueOf(value);
             }else if("double".equals(typeName)){
                 data = Double.valueOf(value);
             }else if("boolean".equals(typeName)){ // "false"  "true"
                 data = Boolean.valueOf(value);
             }else if("java.util.Date".equals(typeName)){
                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                 data=sdf.parse(value);
             }else if("java.lang.String".equals(typeName)){
                 data = value;
             }
             return data;
          }
}


          

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