小怪獸 Java反射技術 等你來打

來源:互聯網
上載者:User

標籤:反射   java   reflection   反射技術   class   

Java反射技術,是java的痛點,也是程式員進化過程中的必打小怪獸,這裡就根據java api好好研究一下java的反射技術。

Class Fields Methods

請先回憶一下【java類的定義】。

一、反射是什麼

反射是什嗎?百度百科這樣定義:JAVA反射機制是在運行狀態下,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態擷取對象資訊以及動態調用對象方法的功能稱為java語言的反射機制。
可以這樣理解,大家小時候有木有玩過平面鏡,在陽光下,傾斜平面鏡把陽光照到黑暗的地方,或對著某個人的眼睛?這就是反射。在java中,反射就是用一個照妖鏡,把藏在java類中的看不到的妖魔鬼怪給找出來。因為類的封裝機制,有些屬性和方法,我們
無法查看和使用,就可以用反射機制動態調用。
二、反射做什麼
1、在運行時判斷任意一個對象所屬的類;
2、在運行時構造任意一個類的對象;
3、在運行時判斷任意一個類所具有的成員變數和方法;
4、在運行時調用任意一個對象的方法;
5、產生動態代理。
簡單的說就是,我們想使用在api中沒有提供介面的類的方法。再簡單點就是,類A有一個方法a,但是在api中沒有提供,我們想使用,就可以用反射技術調用;或則類A有一個屬性b,我們想使用,也可以用反射實現。另一個作用就是,讓我們可以瞭解整個類的結構。
三、反射知識點
1、Class類的執行個體化
在Java中,每個class都有一個相應的Class對象。也就是說,當我們編寫一個類,編譯完成後,在產生的.class檔案中,就會產生一個Class對象,用於表示這個類的類型資訊。
擷取Class類對象的三種方法:
a、object.getClass
   例:String str = "I love you";
       Class c = str.getClass();

b、Class.forName("className")

   例:String className = "java.lang.String";
       Class c = Class.forName(className);

c、Object.class
   例:Class c = String.class;
d、c.getSuperclass();
   例:Class c = String.class;
       Class s = c.getSupreclass();
2、Class類的屬性和方法
若只是想快速使用java反射機制,請自覺跳過本節,直接到第3節看執行個體。
a、方法摘要 (這是從Java api文檔中摘抄過來的所有方法,若感覺繁瑣就跳過本節,直接看我總結的常用方法(見b))
<U> Class<? extends U>
asSubclass(Class<U> clazz)
          強制轉換該 Class 對象,以表示指定的 class 對象所表示的類的一個子類。
T cast(Object obj)
          將一個對象強制轉換成此 Class 對象所表示的類或介面。
boolean desiredAssertionStatus()
          如果要在調用此方法時,將要初始化該類,則返回將分配給該類的斷言狀態。
static Class<?> forName(String className)
          返回與帶有給定字串名的類或介面相關聯的 Class 對象。
static Class<?> forName(String name, boolean initialize, ClassLoader loader)
          使用給定的類載入器,返回與帶有給定字串名的類或介面相關聯的 Class 對象。
<A extends Annotation> A
getAnnotation(Class<A> annotationClass)
          如果存在該元素的指定類型的注釋,則返回這些注釋,否則返回 null。
Annotation[] getAnnotations()
          返回此元素上存在的所有注釋。
String getCanonicalName()
          返回《Java Language Specification》中所定義的基礎類的正常化名稱。
Class[] getClasses()
          返回一個包含某些 Class 對象的數組,這些對象表示屬於此 Class 對象所表示的類的成員的所有公用類和介面,包括從超類和公用類繼承的以及通過該類聲明的公用類和介面成員。
ClassLoader getClassLoader()
          返回該類的類載入器。
Class<?> getComponentType()
          返回表示數組組件類型的 Class。
Constructor<T> getConstructor(Class... parameterTypes)
          返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公用構造方法。
Constructor[] getConstructors()
          返回一個包含某些 Constructor 對象的數組,這些對象反映此 Class 對象所表示的類的所有公用構造方法。
Annotation[] getDeclaredAnnotations()
          返回直接存在於此元素上的所有注釋。
Class[] getDeclaredClasses()
          返回 Class 對象的一個數組,這些對象反映聲明為此 Class 對象所表示的類的成員的所有類和介面,包括該類所聲明的公用、保護、預設(包)訪問及私人類和介面,但不包括繼承的類和介面。
Constructor<T> getDeclaredConstructor(Class... parameterTypes)
          返回一個 Constructor 對象,該對象反映此 Class 對象所表示的類或介面的指定構造方法。
Constructor[] getDeclaredConstructors()
          返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的所有構造方法。
Field getDeclaredField(String name)
          返回一個 Field 對象,該對象反映此 Class 對象所表示的類或介面的指定已聲明欄位。
Field[] getDeclaredFields()
          返回 Field 對象的一個數組,這些對象反映此 Class 對象所表示的類或介面所聲明的所有欄位,包括公用、保護、預設(包)訪問和私人欄位,但不包括繼承的欄位。
Method getDeclaredMethod(String name, Class... parameterTypes)
          返回一個 Method 對象,該對象反映此 Class 對象所表示的類或介面的指定已聲明方法。
Method[] getDeclaredMethods()
          返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或介面聲明的所有方法,包括公用、保護、預設(包)訪問和私人方法,但不包括繼承的方法。
Class<?> getDeclaringClass()
          如果此 Class 對象所表示的類或介面是另一個類的成員,則返回的 Class 對象表示該對象的聲明類。
Class<?> getEnclosingClass()
          返回基礎類的立即封閉類。
Constructor<?> getEnclosingConstructor()
          如果該 Class 對象表示構造方法中的一個本地或匿名類,則返回 Constructor 對象,它表示基礎類的立即封閉構造方法。
Method getEnclosingMethod()
          如果此 Class 對象表示某一方法中的一個本地或匿名類,則返回 Method 對象,它表示基礎類的立即封閉方法。
T[] getEnumConstants()
          如果此 Class 對象不表示枚舉類型,則返回枚舉類的元素或 null。
Field getField(String name)
          返回一個 Field 對象,它反映此 Class 對象所表示的類或介面的指定公用成員欄位。
Field[] getFields()
          返回一個包含某些 Field 對象的數組,這些對象反映此 Class 對象所表示的類或介面的所有可訪問公用欄位。
Type[] getGenericInterfaces()
          返回表示某些介面的 Type,這些介面由此對象所表示的類或介面直接實現。
Type getGenericSuperclass()
          返回表示此 Class 所表示的實體(類、介面、基本類型或 void)的直接超類的 Type。
Class[] getInterfaces()
          確定此對象所表示的類或介面實現的介面。
Method getMethod(String name, Class... parameterTypes)
          返回一個 Method 對象,它反映此 Class 對象所表示的類或介面的指定公用成員方法。
Method[] getMethods()
          返回一個包含某些 Method 對象的數組,這些對象反映此 Class 對象所表示的類或介面(包括那些由該類或介面聲明的以及從超類和超介面繼承的那些的類或介面)的公用 member 方法。
int getModifiers()
          返回此類或介面以整數編碼的 Java 語言修飾符。
String getName()
          以 String 的形式返回此 Class 對象所表示的實體(類、介面、數組類、基本類型或 void)名稱。
Package getPackage()
          擷取此類的包。
ProtectionDomain getProtectionDomain()
          返回該類的 ProtectionDomain。
URL getResource(String name)
          尋找帶有給定名稱的資源。
InputStream getResourceAsStream(String name)
          尋找具有給定名稱的資源。
Object[] getSigners()
          擷取此類的標記。
String getSimpleName()
          返回原始碼中給出的基礎類的簡稱。
Class<? super T> getSuperclass()
          返回表示此 Class 所表示的實體(類、介面、基本類型或 void)的超類的 Class。
TypeVariable<Class<T>>[] getTypeParameters()
          按聲明順序返回 TypeVariable 對象的一個數組,這些對象表示用此 GenericDeclaration 對象所表示的常規聲明來聲明的類型變數。
boolean isAnnotation()
          如果此 Class 對象表示一個注釋類型則返回 true。
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
          如果指定類型的注釋存在於此元素上,則返回 true,否則返回 false。
boolean isAnonymousClass()
          若且唯若基礎類是匿名類時返回 true。
boolean isArray()
          判定此 Class 對象是否表示一個數組類。
boolean isAssignableFrom(Class<?> cls)
          判定此 Class 對象所表示的類或介面與指定的 Class 參數所表示的類或介面是否相同,或是否是其超類或超介面。
boolean isEnum()
          若且唯若該類聲明為原始碼中的枚舉時返回 true。
boolean isInstance(Object obj)
          判定指定的 Object 是否與此 Class 所表示的對象賦值相容。
boolean isInterface()
          判定指定的 Class 對象是否表示一個介面。
boolean isLocalClass()
          若且唯若基礎類是本地類時返回 true。
boolean isMemberClass()
          若且唯若基礎類是成員類時返回 true。
boolean isPrimitive()
          判定指定的 Class 對象是否表示一個基本類型。
boolean isSynthetic()
          如果此類是複合類,則返回 true,否則 false。
T newInstance()
          建立此 Class 對象所表示的類的一個新執行個體。
String toString()
          將對象轉換為字串。
b、常用方法
public String getName()
以 String 的形式返回此 Class 對象所表示的實體(類、介面、數組類、基本類型或 void)名稱。

public ClassLoader getClassLoader()
返回該類的類載入器。有些實現可能使用 null 來表示引導類載入器。如果該類由引導類載入器載入,則此方法在這類實現中將返回 null。

public Package getPackage()
擷取此類的包。此類的類載入器用於尋找該包。如果該類是通過引導類載入器載入的,則搜尋從 CLASSPATH 載入的包的集合,以尋找該類的包。如果所有包對象都不是用該類的類載入器載入的,則返回 null。

public Field[] getFields() throws SecurityException
返回一個包含某些 Field 對象的數組,這些對象反映此 Class 對象所表示的類或介面的所有可訪問公用欄位。返回數組中的元素沒有排序,也沒有任何特定的順序。如果類或介面沒有可訪問的公用欄位,或者表示一個數組類、一個基本類型或 void,則此方法返回長度為 0 的數組。
特別地,如果該 Class 對象表示一個類,則此方法返回該類及其所有超類的公用欄位。如果該 Class 對象表示一個介面,則此方法返回該介面及其所有超介面的公用欄位。
該方法不反映數組類的隱式長度欄位。使用者代碼應使用 Array 類的方法來運算元組。

public Method[] getMethods() throws SecurityException
返回一個包含某些 Method 對象的數組,這些對象反映此 Class 對象所表示的類或介面(包括那些由該類或介面聲明的以及從超類和超介面繼承的那些的類或介面)的公用 member 方法。數組類返回從 Object 類繼承的所有(公用)member 方法。返回數組中的元素沒有排序,也沒有任何特定的順序。如果此 Class 對象表示沒有公用成員方法的類或介面,或者表示一個基本類型或 void,則此方法返回長度為 0 的數組。
類初始化方法 <clinit> 不包含在返回的數組中。如果類聲明了帶有相同參數類型的多個公用成員方法,則它們都會包含在返回的數組中。

public Field getField(String name) throws NoSuchFieldException, SecurityException
返回一個 Field 對象,它反映此 Class 對象所表示的類或介面的指定公用成員欄位。name 參數是一個 String,用於指定所需欄位的簡稱。

public Method getMethod(String name, Class... parameterTypes) throws NoSuchMethodException, SecurityException
返回一個 Method 對象,它反映此 Class 對象所表示的類或介面的指定公用成員方法。name 參數是一個 String,用於指定所需方法的簡稱。parameterTypes 參數是按聲明順序標識該方法形式參數類型的 Class 對象的一個數組。如果 parameterTypes 為 null,則按空數組處理。

public Field[] getDeclaredFields() throws SecurityException
返回 Field 對象的一個數組,這些對象反映此 Class 對象所表示的類或介面所聲明的所有欄位,包括公用、保護、預設(包)訪問和私人欄位,但不包括繼承的欄位。返回數組中的元素沒有排序,也沒有任何特定的順序。如果該類或介面不聲明任何欄位,或者此 Class 對象表示一個基本類型、一個數組類或 void,則此方法返回一個長度為 0 的數組。

public Method[] getDeclaredMethods() throws SecurityException
返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或介面聲明的所有方法,包括公用、保護、預設(包)訪問和私人方法,但不包括繼承的方法。返回數組中的元素沒有排序,也沒有任何特定的順序。如果該類或介面不聲明任何方法,或者此 Class 對象表示一個基本類型、一個數組類或 void,則此方法返回一個長度為 0 的數組。類初始化方法 <clinit> 不包含在返回數組中。如果該類聲明帶有相同參數類型的多個公用成員方法,則它們都包含在返回的數組中。

3、Method類和Field類
a、Method
Method 提供關於類或介面上單獨某個方法(以及如何訪問該方法)的資訊。所反映的方法可能是類方法或執行個體方法(包括抽象方法)。
Method 允許在匹配要調用的實參與基礎方法的形參時進行擴充轉換;但如果要進行收縮轉換,則會拋出 IllegalArgumentException。

public String getName()
以 String 形式返回此 Method 對象表示的方法名稱。

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
對帶有指定參數的指定對象調用由此 Method 對象表示的基礎方法。個別參數被自動解包,以便與基本形參相匹配,基本參數和引用參數都隨需服從方法調用轉換。
如果基礎方法是靜態,那麼可以忽略指定的 obj 參數。該參數可以為 null。
如果基礎方法所需的形參數為 0,則所提供的 args 數組長度可以為 0 或 null。
如果基礎方法是執行個體方法,則使用動態方法尋找來調用它,這一點記錄在 Java Language Specification, Second Edition 的第 15.12.4.4 節中;在發生基於目標對象的運行時類型的重寫時更應該這樣做。
如果基礎方法是靜態,並且尚未初始化聲明此方法的類,則會將其初始化。
如果方法正常完成,則將該方法返回的值返回給調用方;如果該值為基本類型,則首先適當地將其封裝在對象中。但是,如果該值的類型為一組基本類型,則數組元素不 被封裝在對象中;換句話說,將返回基本類型的數組。如果基礎方法傳回型別為 void,則該調用返回 null。

b、Field
Field 提供有關類或介面的單個欄位的資訊,以及對它的動態存取權限。反射的欄位可能是一個類(靜態)欄位或執行個體欄位。
Array 允許在執行 get 或 set 訪問操作期間進行擴充轉換,但如果將發生收縮轉換,則拋出一個 IllegalArgumentException。

public String getName()
返回此 Field 對象表示的欄位的名稱。

Method類和Field類還有很多其他的方法,在這裡就不敘述了,可以查看api文檔。
是不是看的頭疼?不要緊,這個只是瞭解,對於聰明的你,看一下下面的例子就完全明白java的反射機制是什麼。

四、反射怎麼用
//測試類別
public class Programmer {
    private String name;
    private int age;
    private String technology;

    private String program(String s) {
        return this.name + "在使用" + this.technology + "編寫" + s;
    }

    public Programmer() {

    }

    public Programmer(String name, int age, String technology) {
        super();
        this.name = name;
        this.age = age;
        this.technology = technology;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getTechnology() {
        return technology;
    }

    public void setTechnology(String technology) {
        this.technology = technology;
    }
}

//測試
public static void main(String[] args) throws NoSuchMethodException,
            SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException,
            NoSuchFieldException {
        // TODO Auto-generated method stub
        Programmer programmer = new Programmer("zhaicaixiansheng", 23, "java");
        Class c = programmer.getClass();//取得Class對象執行個體
        Method method = c.getDeclaredMethod("program",
                new Class[] { String.class });//第一個參數是方法名,第二個是所需參數類型的class。得到Programmer所定義的方法(任何方法都可以)
        if (method != null) {
            method.setAccessible(true);//設定為true
            String info = (String) method.invoke(programmer,
                    new String[] { "奔跑吧兄弟" });//使用方法,第一個參數是對象名,第二個是參數
            System.out.println(info);
        }

        Field field = c.getDeclaredField("name");
        if (field != null) {
            field.setAccessible(true);
            field.set(programmer, "摘菜先生");
        }

        String info = (String) method.invoke(programmer,
                new String[] { "奔跑吧兄弟" });
        System.out.println(info);
    }

五、反射優缺點
優點:增加程式的靈活性;動態調用;豐富程式介面等。
缺點:破壞程式的封裝特性;使用麻煩;
六、注意和總結
注意:雖然反射技術可以增加程式的靈活性,可以動態調用類中看不見的方法和屬性,但是,不能過於依賴反射,這樣會造成,程式結構混亂,代碼可讀性下降,安全性差等。
總結:編程其實很容易,哪裡不會搜哪裡。
I love programming. I love you.

小怪獸 Java反射技術 等你來打

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.