Java反射機制(三):調用對象的私有屬性和方法


一、 通過反射調用類中的方法

在正常情況下,得到類的對象后,我們就可以直接調用類中的方法了,如果要想調用的話,則肯定必須清楚地知道要調用的方法是什么,之后通過Class類中的getMethod方法,可得到Method對象。

public Method getMethod(String name,
                        Class<?>... parameterTypes)
                 throws NoSuchMethodException,
                        SecurityException
當獲取到Method對象后,可以通過該對象來執行方法,但是在方法調用的時候,因為會牽扯到方法中參數的問題,所以通過getMethod()取得的時候,必須設置好參數類型。

package org.chen.yuan.reflect;
interface China{	// 定義China接口
	public static final String NATIONAL = "China" ;	// 定義全局常量
	public static final String AUTHOR = "李興華" ;	// 定義全局常量
	public void sayChina() ;		// 無參的,沒有返回值的方法
	public String sayHello(String name,int age) ;	// 定義有兩個參數的方法,並返回內容
}
public class Person implements China{
	private String name ;
	private int age ;
	public Person(){	// 無參構造
	}
	public Person(String name){
		this.name = name ;	// 設置name屬性
	}
	public Person(String name,int age){
		this(name) ;
		this.age = age ;
	}
	public void sayChina(){	// 覆寫方法
		System.out.println("作者:" + AUTHOR + ",國籍:" + NATIONAL) ;
	}
	public String sayHello(String name,int age){
		return name + ",你好!我今年:" + age + "歲了!" ;
	}
	public void setName(String name){
		this.name = name ;
	}
	public void setAge(int age){
		this.age = age ;
	}
	public String getName(){
		return this.name ;
	}
	public int getAge(){
		return this.age ;
	}
};
我們調用sayChina()方法,此方法中沒有任何參數。

執行調用的方法,需通過Method的invoke方法來實現:

public Object invoke(Object obj,
                     Object... args)
              throws IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException

示例1:(調用無參的方法)

package org.chen.yuan.reflect;
interface China{	// 定義China接口
	public static final String NATIONAL = "China" ;	// 定義全局常量
	public static final String AUTHOR = "沉緣" ;	// 定義全局常量
	public void sayChina() ;		// 無參的,沒有返回值的方法
	public String sayHello(String name,int age) ;	// 定義有兩個參數的方法,並返回內容
}
public class Person implements China{
	private String name ;
	private int age ;
	public Person(){	// 無參構造
	}
	public Person(String name){
		this.name = name ;	// 設置name屬性
	}
	public Person(String name,int age){
		this(name) ;
		this.age = age ;
	}
	public void sayChina(){	// 覆寫方法
		System.out.println("作者:" + AUTHOR + ",國籍:" + NATIONAL) ;
	}
	public String sayHello(String name,int age){
		return name + ",你好!我今年:" + age + "歲了!" ;
	}
	public void setName(String name){
		this.name = name ;
	}
	public void setAge(int age){
		this.age = age ;
	}
	public String getName(){
		return this.name ;
	}
	public int getAge(){
		return this.age ;
	}
};

我們在Person.java 類中定義了一個無參方法sayChina和一個有參數的方法sayHello,接下來,我們調用無參數的方法:

package org.chen.yuan.reflect;

import java.lang.reflect.Method;

public class InvokeSyaChinaDemo
{
    public static void main(String[] args)
    {
        Class<?> c1 = null;
        try
        {
            c1 = Class.forName("org.chen.yuan.reflect.Person");
            Method met = c1.getMethod("sayChina");
            met.invoke(c1.newInstance());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

輸出: 作者:沉緣,國籍:China

可以看出,通過上述反射的方式,我們能夠順利的調用Person類中的方法。 那思考下,如果我們要調用含有參數的方法sayHello,該如何做呢?

可以想象,如果方法里存在了參數,則必須設置參數的類型及內容。

public class InvokeSayHelloDemo
{

    public static void main(String[] args) throws Exception
    {
        Class<?> c1 = null;
        c1 = Class.forName("org.chen.yuan.reflect.Person");
        Method met = c1.getMethod("sayHello", String.class, int.class);
        String result = (String) met.invoke(c1.newInstance(), "沉緣", 25);
        System.out.println(result);
    }

}

輸出: 沉緣,你好!我今年:25歲了!


二、 通過反射調用類中的setter及getter方法

setter和getter方法是訪問類屬性的標准方法,如果一個類中的屬性被封裝,則必須通過setter及getter方法設設置和取得,實際上此方法的操作之所以要這樣規定,主要是由於反射機制可以給予支持。

通過反射可以調用setter及getter方法。

package org.chen.yuan.reflect;

import java.lang.reflect.Method;

public class InvokeSetGetDemo
{

    public static void main(String[] args) throws Exception
    {
        Class<?> c1 = null;
        Object obj = null;

        c1 = Class.forName("org.chen.yuan.reflect.Person");
        obj = c1.newInstance();

        setter(obj, "name", "沉緣", String.class);
        getter(obj, "name");

        setter(obj, "age", 25, int.class);
        getter(obj, "age");
    }

    /**
     * @param obj 要操作的對象
     * @param att 要操作的屬性
     * @param value 要設置的屬性數據
     * @param type 要設置的屬性的類型
     */
    public static void setter(Object obj, String att, Object value, Class<?> type)
    {
        try
        {
            Method met = obj.getClass().getMethod("set" + initStr(att), type);
            met.invoke(obj, value);

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * @param obj 要操作的對象
     * @param att 要操作的屬性
     */
    public static void getter(Object obj, String att) throws Exception
    {
        Method met = obj.getClass().getMethod("get" + initStr(att));
        System.out.println(met.invoke(obj));
    }

    /**
     * 將單詞首字母大寫
     * 
     * @param old
     * @return
     */
    public static String initStr(String old)
    {
        String newStr = old.substring(0, 1).toUpperCase() + old.substring(1);
        return newStr;
    }
}


三、 通過反射調用屬性

如果假設要操作一個類中的屬性,則也可以通過Field完成,而不必麻煩的通過setter和getter。Class類中,獲取類中Field的方法:

1) 得到類中公共屬性

public Field getField(String name)
               throws NoSuchFieldException,
                      SecurityException
2)得到本類屬性

public Field getDeclaredField(String name)
                       throws NoSuchFieldException,
                              SecurityException

而在Field類中,提供了獲取屬性內容及設置屬性內容的方法:

1) 獲取屬性內容

public Object get(Object obj)
           throws IllegalArgumentException,
                  IllegalAccessException
2) 設置屬性內容

public void set(Object obj,
                Object value)
         throws IllegalArgumentException,
                IllegalAccessException

還有一點需要注意,訪問類中的私有屬性時,必須要讓該屬性對外可見:

public void setAccessible(boolean flag)
                   throws SecurityException
該方法繼承自Field的父類:
java.lang.reflect

Class AccessibleObject

只要把該方法的參數內容設置為true即可。

public class InvokeFieldDemo
{
    public static void main(String args[]) throws Exception
    {
        Class<?> c1 = null;
        Object obj = null;
        c1 = Class.forName("org.chen.yuan.reflect.Person"); // 實例化Class對象
        obj = c1.newInstance();
        Field nameField = null;
        Field ageField = null;
        nameField = c1.getDeclaredField("name"); // 取得name屬性
        ageField = c1.getDeclaredField("age"); // 取得name屬性
        nameField.setAccessible(true); // 此屬性對外部可見
        ageField.setAccessible(true); // 此屬性對外部可見
        nameField.set(obj, "沉緣"); // 設置name屬性內容
        ageField.set(obj, 25); // 設置age屬性內容
        System.out.println("姓名:" + nameField.get(obj));
        System.out.println("年齡:" + ageField.get(obj));
    }
};


輸出: 

姓名:沉緣
年齡:25

可見,操作屬性,未必需要setter和getter方法的支持,但是,為了保證程序的安全性,最好還是通過setter和getter方法對屬性進行操作。


四、 通過反射操作數組

反射機制不光能用在類中,也可以應用在任意的引用數據類型上,當然,這就包含了數組,數組使用Array類完成。

Class類中存在以下一個方法:

public Class<?> getComponentType()

Array類中得到數組指定下標的內容:

public static Object get(Object array,
                         int index)
                  throws IllegalArgumentException,
                         ArrayIndexOutOfBoundsException

Array類中修改內容:

public static void set(Object array,
                       int index,
                       Object value)
                throws IllegalArgumentException,
                       ArrayIndexOutOfBoundsException

Array類中開辟新的數組:

public static Object newInstance(Class<?> componentType,
                                 int... dimensions)
                          throws IllegalArgumentException,
                                 NegativeArraySizeException

取得數組信息並修改內容:

package org.chen.yuan.reflect;
import java.lang.reflect.Array ;
public class ClassArrayDemo{
	public static void main(String args[]) throws Exception{
		int temp[] = {1,2,3} ;// 聲明一整型數組
		Class<?> c = temp.getClass().getComponentType() ;	// 取得數組的Class對象
		System.out.println("類型:" + c.getName()) ;	// 取得數組類型名稱
		System.out.println("長度:" + Array.getLength(temp)) ;
		System.out.println("第一個內容:" + Array.get(temp,0)) ;
		Array.set(temp,0,6) ;
		System.out.println("第一個內容:" + Array.get(temp,0)) ;
	}
};
輸出:

類型:int
長度:3
第一個內容:1
第一個內容:6


數組修改的過程,實際上就是創建一個新的數組的過程,所以要把舊的數組內容拷貝到新的數組中去。

package org.chen.yuan.reflect;

import java.lang.reflect.Array;

public class ChangeArrayDemo
{
    public static void main(String args[]) throws Exception
    {
        int temp[] = {1, 2, 3};// 聲明一整型數組
        int newTemp[] = (int[]) arrayInc(temp, 5); // 重新開辟空間5
        print(newTemp);
        System.out.println("\n-------------------------");
        String t[] = {"chenyuan", "wuqing", "lengxue"};
        String nt[] = (String[]) arrayInc(t, 8);
        print(nt);
    }

    public static Object arrayInc(Object obj, int len)
    {
        Class<?> c = obj.getClass();
        Class<?> arr = c.getComponentType(); // 得到數組的
        Object newO = Array.newInstance(arr, len); // 開辟新的大小
        int co = Array.getLength(obj);
        System.arraycopy(obj, 0, newO, 0, co); // 拷貝內容
        return newO;
    }

    public static void print(Object obj)
    { // 數組輸出
        Class<?> c = obj.getClass();
        if (!c.isArray())
        { // 判斷是否是數組
            return;
        }
        Class<?> arr = c.getComponentType();
        System.out.println(arr.getName() + "數組的長度是:" + Array.getLength(obj)); // 輸出數組信息
        for (int i = 0; i < Array.getLength(obj); i++)
        {
            System.out.print(Array.get(obj, i) + "、"); // 通過Array輸出
        }
    }
};

輸出:

nt數組的長度是:5
1、2、3、0、0、
-------------------------
java.lang.String數組的長度是:8
chenyuan、wuqing、lengxue、null、null、null、null、null、



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM