天天看點

你必須知道的Java反射機制缺點:

反射

JAVA 反射機制是在運作狀态中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動态擷取的資訊以及動态調用對象的方法的功能稱為 java 語言的反射機制。

反射可以用來生成動态代理。

反射機制的相關類

對于一個位元組碼檔案.class,雖然表面上我們對該位元組碼檔案一無所知,但該檔案本身卻記錄了許多資訊。Java在将.class位元組碼檔案載入時,JVM将産生一個java.lang.Class對象代表該.class位元組碼檔案,從該Class對象中可以獲得類的許多基本資訊,這就是反射機制。是以要想完成反射操作,就必須首先認識Class類。

反射機制所需的類主要有java.lang包中的Class類和java.lang.reflect包中的Constructor類、Field類、Method類和Parameter類。Class類是一個比較特殊的類,它是反射機制的基礎,Class類的對象表示正在運作的Java程式中的類或接口,也就是任何一個類被加載時,即将類的.class檔案(位元組碼檔案)讀入記憶體的同時,都自動為之建立一個java.lang.Class對象。Class類沒有公共構造方法,其對象是JVM在加載類時通過調用類加載器中的defineClass()方法建立的,是以不能顯式地建立一個Class對象。通過這個Class對象,才可以獲得該對象的其他資訊。

每個類被加載之後,系統都會為該類生成一個對應的Class對象,通過Class對象就可以通路到JVM中該類的資訊,一旦類被加載到JVM中,同一個類将不會被再次載入。被載入JVM的類都有一個唯一辨別就是該類的全名,即包括包名和類名。在Java中程式獲得Class對象有如下3種方式。

(1)使用Class類的靜态方法forName(String className),其中參數className表示所需類的全名。如“Class cObj=Class.forName(“java.lang.String”);”。另外,forName()方法聲明抛出ClassNotFoundException異常,是以調用該方法時必須捕獲或抛出該異常。

(2)用類名調用該類的class屬性來獲得該類對應的Class對象,即“類名.class”。如,語句“Class cObj=Cylinder.class;”将傳回Cylinder類所對應的Class對象賦給cObj變量。

(3)用對象調用getClass()方法來獲得該類對應的Class對象,即“對象.getClass()”。該方法是Object類中的一個方法,是以所有對象調用該方法都可以傳回所屬類對應的Class對象。如例8.8中的語句“Person per=new Person(“張三”);”可以通過以下語句傳回該類的Class對象:Class cObj=per.getClass();

通過類的class屬性獲得該類所對應的Class對象,會使代碼更安全,程式性能更好,是以大部分情況下建議使用第二種方式。但如果隻獲得一個字元串,例如獲得String類對應的Class對象,則不能使用String.class方式,而是使用Class.forName(“java.lang.String”)。注意:如果要想獲得基本資料類型的Class對象,可以使用對應的打包類加上.TYPE,例如,Integer.TYPE可獲得int的Class對象,但要獲得Integer.class的Class對象,則必須使用Integer.class。在獲得Class對象後,就可以使用getClass()方法來取得Class對象的基本資訊。

反射包reflect中的常用類

反射機制中除了上面介紹的java.lang包中的Class類之外,還需要java.lang.reflect包中的Constructor類、Method類、Field類和Parameter類。Java 8以後在java.lang.reflect包中新增了一個Executable抽象類,該類對象代表可執行的類成員。Executable抽象類派生了Constructor和Method兩個子類。

java.lang.reflect.Executable類提供了大量方法用來擷取參數、修飾符或注解等資訊,以下是Executable的一些常用方法

//傳回所有參數的類型

public abstract Class<?>[] getParameterTypes();

//傳回參數的個數

public int getParameterCount()

//傳回所有形參

public Parameter[] getParameters()

//傳回整數表示的修飾符關鍵字常量

public abstract int getModifiers();

getModifiers()方法傳回的是以整數表示的修飾符。此時引入Modifier類,通過調用Modifier.toString(int mod)方法傳回修飾符常量所應的字元串。

例如:Modifier.FINAL

public static final int FINAL = 0x00000010;//是用16進制的0x00000010表示Modifier.FINAL,數值為16

java.lang.reflect.Constructor類是java.lang.reflect.Executable類的直接子類,用于表示類的構造方法。通過Class對象的getConstructors()方法可以獲得目前運作時類的構造方法。以下是Constructor類的常用方法

//傳回構造函數的名稱

public String getName() {

return getDeclaringClass().getName();

}

public T newInstance(Object … initargs)通過該構造方法建立一個對象,initargs為構造方法所需的參數,如果沒有參數則表示無參構造方法

public void setAccessible(boolean flag) //私有構造方法是不允許通過反射來建立對象的,但是如果先執行該方法,傳參為true,則允許建立

java.lang.reflect.Method類是java.lang.reflect.Executable類的直接子類,用于封裝成員方法的資訊,調用Class對象的getMethod()方法或getMethods()方法可以獲得目前運作時類的指定方法或所有方法。以下是java.lang.reflect.Method類的常用方法。

//傳回方法的名稱

public String getName()

//跟構造方法的setAccessible用法一樣

public void setAccessible(boolean flag)

//傳回方法傳回值的類型

public Class<?> getReturnType() {

return returnType;

}

//重寫的equals方法

public boolean equals(Object obj) {

//判斷傳入對象是否為空,是否為Method類型

if (obj != null && obj instanceof Method) {

Method other = (Method)obj;

//比較類型和名字

if ((getDeclaringClass() == other.getDeclaringClass())

&& (getName() == other.getName())) {

//比較傳回值

if (!returnType.equals(other.getReturnType()))

return false;

//比較參數是否相等

return equalParamTypes(parameterTypes, other.parameterTypes);

}

}

return false;

}

//使用傳入的對象和參數執行方法

public Object invoke(Object obj, Object… args)

throws IllegalAccessException, IllegalArgumentException,

InvocationTargetException

java.lang.reflect.Field類用于封裝成員變量資訊,調用Class對象的getField()方法或getFields()可以獲得目前運作時類的指定成員變量或所有成員變量。java.lang.reflect.Field類的常用方法如下面所示。

//傳回成員變量的名稱

public String getName() {

return name;

}

//傳回成員變量的類型

public Class<?> getType() {

return type;

}

//傳回成員變量的值

public Object get(Object obj)

throws IllegalArgumentException, IllegalAccessException

//傳回成員變量的值

public boolean getBoolean(Object obj)

throws IllegalArgumentException, IllegalAccessException

//給對象中的成員變量指派

public void set(Object obj, Object value)

throws IllegalArgumentException, IllegalAccessException

//傳回成員變量的類型

public Class<?> getType() {

return type;

}

java.lang.reflect.Parameter類是參數類,每個Parameter對象代表方法的一個參數。java.lang.reflect.Parameter類中提供了許多方法來擷取參數資訊,以下是java.lang.reflect.Parameter類的常用方法。

//傳回修飾符的數值表示,使用Modifier的toString方法,可以得到名稱

public int getModifiers() {

return modifiers;

}

//傳回參數的名稱

public String getName() {

return name;

}

//傳回參數的類型

public Class<?> getType()

//是否為可變參數

public boolean isVarArgs() {

return executable.isVarArgs() &&

index == executable.getParameterCount() - 1;

}

例子

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
        /**
         * 擷取TestObject類的Class對象并且建立TestObject類執行個體
         */
        Class<?> tagetClass = Class.forName("cn.mytest.TestObject");
        TestObject targetObject = (TestObject) tagetClass.newInstance();
        /**
         * 擷取所有類中所有定義的方法
         */
        Method[] methods = tagetClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }
        /**
         * 擷取指定方法并調用
         */
        Method publicMethod = tagetClass.getDeclaredMethod("testMethod",
                String.class);

        publicMethod.invoke(targetObject, "hello world");
        /**
         * 擷取指定成員變量并進行修改
         */
        Field field = tagetClass.getDeclaredField("name");
        //為了對類中的參數進行修改我們取消安全檢查
        field.setAccessible(true);
        field.set(targetObject, "my");
        /**
         * 調用 private 方法
         */
        Method privateMethod = tagetClass.getDeclaredMethod("privateMethod");
        //為了調用private方法我們取消安全檢查
        privateMethod.setAccessible(true);
        privateMethod.invoke(targetObject);
    }
}

           

注意:

getxxxx和getDeclaredxxxx的差別在于是否傳回父類的相關資訊,下面是getMethods()和getDeclaredMethods()的差別

1:getMethods(),該方法是擷取本類以及父類或者父接口中所有的公共方法(public修飾符修飾的)

2:getDeclaredMethods(),該方法是擷取本類中的所有方法,包括私有的(private、protected、預設以及public)的方法。(不包含父類方法)

優點:

反射機制極大的提高了程式的靈活性和擴充性,降低子產品的耦合性,提高自身的适應能力。

通過反射機制可以讓程式建立和控制任何類的對象,無需提前寫死目标類。

使用反射機制能夠在運作時構造一個類的對象、判斷一個類所具有的成員變量和方法、調用一個對象的方法。

反射機制是建構架構技術的基礎所在,使用反射可以避免将代碼寫死在架構中。

正是反射有以上的特征,是以它能動态編譯和建立對象,極大的激發了程式設計語言的靈活性,強化了多态的特性,進一步提升了面向對象程式設計的抽象能力,因而受到程式設計界的青睐

缺點:

1、性能較差

2、安全問題