前面介紹瞭如何利用反射技術讀寫私有屬性,不單是私有屬性,就連私有方法也能經過反射技術來調用。爲了演示反射的逆天功能,首先給Chicken雞類增長下列幾個私有方法,簡單起見弄來了set***/get***這樣的基本方法:html
private void setName(String name) { // 設置名稱 this.name = name; } private String getName() { // 獲取名稱 return this.name; } private void setSex(int sex) { // 設置性別 this.sex = sex; } private int getSex() { // 獲取性別 return this.sex; }
參照私有屬性的反射操做過程,私有方法的反射調用可分解爲以下三個步驟:java
一、調用Class對象的getDeclaredMethod方法,獲取指定名稱的方法對象,即Method對象;
二、調用Method對象的setAccessible方法,並傳入true值,表示將該方法設置爲容許訪問,以解除private的限制;
三、調用Method對象的invoke方法,並傳入雞類實例,酌情填寫輸入參數;
雖然方法只有調用一說,沒有讀寫之分,可是方法的輸入參數能夠有也能夠沒有,一樣輸出參數能夠有也能夠沒有,於是對於方法對象而言,反射技術須要支持如下四種狀況:有輸入參數、無輸入參數、有輸出參數、無輸出參數。注意到Chicken類的新增方法getName無輸入參數、有輸出參數,setName有輸入參數、無輸出參數,故而只要實現getName與setName兩個方法的反射調用,恰好就覆蓋了有無入參和有無出參這四種場景。
先來看getName,由於該方法沒有輸入參數,因此反射調用相對簡單,只是invoke方法的返回值爲Object類型,須要強制轉換成String類型,這樣才能得到雞的名稱。此時獲取名稱的反射代碼以下所示:this
// 經過反射來調用某個實例的私有方法(getName方法) private static String getReflectName(Chicken chicken) { String name = ""; try { Class cls = Chicken.class; // 得到Chicken類的基因類型 // 經過方法名稱及參數列表獲取該方法的Method對象 Method method = cls.getDeclaredMethod("getName"); method.setAccessible(true); // 將該方法設置爲容許訪問 name = (String) method.invoke(chicken); // 調用某實例的方法並得到輸出參數 } catch (Exception e) { // 捕捉到了任何一種異常(錯誤除外) e.printStackTrace(); } return name; }
再來看setName,因爲該方法存在輸入參數,所以調用Class對象的getDeclaredMethod之時,須要傳入參數類型列表。之因此這麼作,是由於同名方法可能會被屢次重載,重載後的方法經過參數個數與參數類型加以區分。另外,invoke方法也要傳入setName方法所需的各項參數值。一系列調整以後,設置名稱的反射代碼改寫以下:日誌
// 經過反射來調用某個實例的私有方法(setName方法) private static void setReflectName(Chicken chicken, String name) { try { Class cls = Chicken.class; // 得到Chicken類的基因類型 // 經過方法名稱及參數列表獲取該方法的Method對象 // 之因此須要參數類型列表,是由於同名方法可能會被屢次重載,重載後的方法經過參數個數與參數類型加以區分 Method method = cls.getDeclaredMethod("setName", String.class); method.setAccessible(true); // 將該方法設置爲容許訪問 method.invoke(chicken, name); // 攜帶輸入參數調用某實例的方法 } catch (Exception e) { // 捕捉到了任何一種異常(錯誤除外) e.printStackTrace(); } }
編寫完成名稱獲取與名稱設置的反射代碼,獲取性別與設置性別的反射代碼便可如法炮製,區別主要有兩處,一處的強制類型轉換把「(int)」換成「(String)」,另外一處的參數類型列表把「String.class」換成「int.class」。性別獲取與性別設置的反射代碼示例以下:htm
// 經過反射來調用某個實例的私有方法(getSex方法) private static int getReflectSex(Chicken chicken) { int sex = -1; try { Class cls = Chicken.class; // 得到Chicken類的基因類型 // 經過方法名稱及參數列表獲取該方法的Method對象 Method method = cls.getDeclaredMethod("getSex"); method.setAccessible(true); // 將該方法設置爲容許訪問 sex = (int) method.invoke(chicken); // 調用某實例的方法並得到輸出參數 } catch (Exception e) { // 捕捉到了任何一種異常(錯誤除外) e.printStackTrace(); } return sex; } // 經過反射來調用某個實例的私有方法(setSex方法) private static void setReflectSex(Chicken chicken, int sex) { try { Class cls = Chicken.class; // 得到Chicken類的基因類型 // 經過方法名稱及參數列表獲取該方法的Method對象 // 之因此須要參數類型列表,是由於同名方法可能會被屢次重載,重載後的方法經過參數個數與參數類型加以區分 Method method = cls.getDeclaredMethod("setSex", int.class); method.setAccessible(true); // 將該方法設置爲容許訪問 method.invoke(chicken, sex); // 攜帶輸入參數調用某實例的方法 } catch (Exception e) { // 捕捉到了任何一種異常(錯誤除外) e.printStackTrace(); } }
而後輪到外部調用這幾個封裝好的反射方法了,準備把公雞實例的名稱改成「母鴨」,性別改成「雌性」,具體的調用代碼以下所示:對象
Cock cock = new Cock(); // 建立一個公雞實例 System.out.println("準備修理公雞, 名稱 = "+getReflectName(cock)+", 性別 = "+getReflectSex(cock)); setReflectName(cock, "母鴨"); // 把公雞實例的名稱篡改成「母鴨」 setReflectSex(cock, cock.FEMALE); // 把公雞實例的性別篡改成「雌性」 System.out.println("結束脩理公雞, 名稱 = "+getReflectName(cock)+", 性別 = "+getReflectSex(cock));
運行上面的演示代碼,觀察到下面的日誌信息,可見一隻雄赳赳的公雞被硬生生整成了母鴨模樣:blog
準備修理公雞, 名稱 = 公雞, 性別 = 0 結束脩理公雞, 名稱 = 母鴨, 性別 = 1
更多Java技術文章參見《Java開發筆記(序)章節目錄》開發