天天看點

反射調用方法 | 帶你學《Java語言進階特性》之八十六

上一篇:反射擷取類結構資訊 | 帶你學《Java語言進階特性》之八十五

【本節目标】

本節通過之前的案例介紹了反射調用構造方法和普通方法,實作反射執行個體化對象,并通過反射來擷取類之中的全部方法。

反射調用構造方法

在一個類中除了有繼承的關系外,最為重要的操作就是類中的結構處理了,而類中的結構首先需要觀察的就是構造方法的使用問題,實際上在之前通過反射執行個體化對象的時候就已經接觸到構造方法的問題了:

執行個體化方法替代:

clazz.getDeclaredConstructor().newInstance()

所有類的構造方法的擷取都可以直接通過Class類來完成,該類中定義有如下的幾個方法:

擷取所有構造方法:

public Constructor<?>[] getDeclaredConstructors() throws SecurityException

擷取指定構造方法:

public Constructor<T> getConstructor​() throws SecurityException

public Constructor<?>[] getConstructors()throws SecurityException

public Constructor<T> getConstructor​(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

範例:修改Person類的定義

AbstractBase:

public abstract class AbstractBase {
    public AbstractBase() {}
    public AbstractBase(String msg) {}
}           

Person:

public class Person extends AbstractBase implements  IChannelService,IMessageService {
    public Person() {}
    public Person(String name, int age) {
}           

範例:擷取構造

JavaAPIDemo

public class JavaAPIDemo {
    public static void main(String[] args) {
        Class<?> cls = Person.class;//擷取指定類的Class對象
        Constructor<?>[] constructors = cls.getDeclaredConstructors();  //擷取全部構造
        for (Constructor<?> cons : constructors) {
            System.out.println(cons);
        }
    }
}

//執行結果:public cn.mldn.vo.Person()
//public cn.mldn.vo.Person(java.lang.String,int)           

此時擷取的是類中的全部構造方法,但是也可以擷取一個指定參數的構造。例如:現在的Person類之中有兩個構造方法:

修改Person:

public class Person extends AbstractBase implements  IChannelService,IMessageService {
    private String name;
    private int age;
    public Person() {}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "姓名:" + this.name + "、年齡:" + this.age;
    }
}           

此時調用Person類中的有參構造方法進行Person類對象執行個體化,此時必須指明要調用的構造,而後通過Constructor類中提供的執行個體化方法操作:

public T newInstance​(Object... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException

範例:調用指定構造執行個體化對象

import java.lang.reflect.Constructor;

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;
        Constructor<?> constructor = cls.getConstructor(String.class, int.class);
        Object obj = constructor.newInstance("小強", 78);
        System.out.println(obj);  //姓名:小強、年齡:78
    }
}           

雖然程式代碼本身允許開發者調用有參構造,但是如果從實際的開發角度出發,所有使用反射的類中最好使用無參構造,因為這樣的執行個體化可以達到統一性。

反射調用普通方法

在進行反射處理的時候也可以通過反射來擷取類之中的全部方法,但是需要提醒的是,如果想通過反射調用這些方法,必須有一個前提條件:類之中要提供執行個體化對象。

在Class類中提供了如下的操作可以擷取方法對象:

擷取全部方法:

public Method[] getMethods() throws SecurityException

擷取指定方法:

public Method getMethod​(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

擷取本類全部方法:

public Method[] getDeclaredMethods() throws SecurityException

擷取本類指定方法:

public Method getDeclaredMethod​(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

範例:擷取全部方法

import java.lang.reflect.Method;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;
        {
            Method methods [] = cls.getMethods();//擷取全部方法(包括父類中的方法)
            for (Method met : methods){
                System.out.println(met);
            }
        }
        System.out.println("---------------難以忘懷的愚人節的分割線-----------")
        {  // 擷取本類方法
            Method methods [] = cls.getDeclaredMethods();
            for (Method met : methods){
                System.out.println(met);
        }

        }
    }           

執行結果:

反射調用方法 | 帶你學《Java語言進階特性》之八十六

但是需要注意的是,這時的方法資訊的擷取是依靠Method類提供的toString()方法完成的,很多時候也可以由使用者來拼湊方法資訊的展示形式。

範例:自定義方法資訊顯示

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;  // 擷取指定類的Class對象
        Method methods[] = cls.getMethods();
        for (Method met:methods){
            int mod=met.getModifiers();//修飾符
            System.out.print(Modifier.toString(mod)+" ");
            System.out.print(met.getReturnType().getName()+" ");
            System.out.print(met.getName()+"(");
            Class<?> params [] = met.getParameterTypes();//擷取參數類型
            for (int x = 0; x < params.length; x ++) {
                System.out.print(params[x].getName() + " " + "arg-" + x);
                if(x < params.length - 1) {
                    System.out.println(",");
                }
            }
            System.out.print(")");
            class<?> exp [] = met.getExceptionTypes();
            if(exp.length > 0) {
                System.out.print("throws");
            }
            for(int x = 0 ; x < exp.length ; x ++) {
                System.out.print(exp[x].getName());
                if(x < exp.length - 1) {
                    System.out.println(",");
                }
            }
            System.out.println();  //換行
        }
    }
}           

這種代碼隻需要清楚可以根據反射擷取方法的結構即可,不需要過多深入了解,但在Method類中有一個緻命的重要方法:

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

在Person類裡面為name屬性追加有setter、getter方法。

public class Person extends AbstractBase implements  IChannelService,IMessageService {
    private String name;
    private int age;
    public Person() {}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    // 其他操作代碼略
}           

需要通過反射機制來實作Person類之中的setter與getter方法的調用處理。

範例:在不導入指定類開發包的情況下實作屬性的配置

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("cn.mladn.vo.Person"); //擷取指定類的Class對象
        // String attribute = "name"; //要操作的類屬性
        String value = "小強子";  //要設定的屬性内容
        //1.任何情況下如果要想儲存類中的屬性或者調用類中的方法都必須保證存在有執行個體化對象,既然不允許導入包,那麼就反射執行個體化
        Object obj = cls.getDeclaredConstructor().newInstance();  //調用無參構造執行個體化
        //2.如果要想進行方法的調用,那麼一定要擷取方法的名稱
        String setMethodName = "setName";  //方法名稱 
        Method method = cls.getDeclaredMethodsetMethodName, String.class);   //擷取指定的方法
        method.invoke(obj, value);  //等價于:Person對象.setName(value);
        String getMethodName="getName";
        method=cls.getDeclaredMethod(getMethodName);   //getter沒有參數
        System.out.println(getMethod.invoke(obj););  //等價于:Person對象.getName();
    } 
}           

利用此類操作整體的形式上不會有任何明确的類對象産生,一切都是依靠反射機制處理的,這樣的處理避免了與某一個類耦合問題。

想學習更多的Java的課程嗎?從小白到大神,從入門到精通,更多精彩不容錯過!免費為您提供更多的學習資源。

本内容視訊來源于

阿裡雲大學 下一篇:反射調用成員 | 帶你學《Java語言進階特性》之八十七 更多Java面向對象程式設計文章檢視此處