Java開發筆記(八十)利用反射技術操做私有方法

前面介紹瞭如何利用反射技術讀寫私有屬性,不單是私有屬性,就連私有方法也能經過反射技術來調用。爲了演示反射的逆天功能,首先給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開發筆記(序)章節目錄開發

相關文章
相關標籤/搜索