Java--反射機制(一)——反射 API

一履因、概述

1丐谋、Java反射機制(Java-Reflect):

在運行狀態(tài)中,對于任意一個類饲握,都能夠知道這個類中的所有屬性和方法私杜;對于任意一個對象,都能夠調用它的任意一個方法和屬性互拾;這種動態(tài)獲取的信息以及動態(tài)調用對象的方法的功能稱為java的反射機制歪今。

反射是Java開發(fā)中一個非常重要的概念,掌握了反射的知識颜矿,才能更好的學習Java高級課程.

2寄猩、Java 反射機制的功能
  1. 在運行時判斷任意一個對象所屬的類。

  2. 在運行時構造任意一個類的對象骑疆。

  3. 在運行時判斷任意一個類所具有的成員變量和方法田篇。

  4. 在運行時調用任意一個對象的方法。

  5. 生成動態(tài)代理箍铭。

3泊柬、Java 反射機制的應用場景
  1. 逆向代碼 ,例如反編譯

  2. 與注解相結合的框架 例如Retrofit

  3. 單純的反射機制應用框架 例如EventBus

  4. 動態(tài)生成類框架 例如Gson

二诈火、通過Java反射查看類信息

1兽赁、獲得Class對象

每個類被加載之后,系統(tǒng)就會為該類生成一個對應的Class對象冷守。通過該Class對象就可以訪問到JVM中的這個類刀崖。

在Java程序中獲得Class對象通常有如下三種方式:

  1. 使用 Class 類的forName(String clazzName)靜態(tài)方法。該方法需要傳入字符串參數(shù)拍摇,該字符串參數(shù)的值是某個類的全限定名(必須添加完整包名)亮钦。

  2. 調用某個類的class屬性來獲取該類對應的 Class 對象。

  3. 調用某個對象的getClass()方法充活。該方法是java.lang.Object類中的一個方法蜂莉。

//第一種方式 通過Class類的靜態(tài)方法——forName()來實現(xiàn)
class1 = Class.forName("com.lvr.reflection.Person");
//第二種方式 通過類的class屬性
class1 = Person.class;
//第三種方式 通過對象getClass方法
Person person = new Person();
Class<?> class1 = person.getClass();

對于方式一和方式二都是直接根據(jù)類來取得該類的 Class 對象蜡娶,相比之下,方式二有如下的兩種優(yōu)勢:

  • 代碼跟安全映穗。程序在編譯階段就能夠檢查需要訪問的 Class 對象是否存在窖张。
  • 線程性能更好。因為這種方式無須調用方法男公,所以性能更好荤堪。

可以通過類的類類型創(chuàng)建該類的對象實例。

Class.newInstance();
//
Foot foot = (Foot) c1.newInstance();
2枢赔、從 Class 中獲取信息

一旦獲得了某個類所對應的Class 對象之后澄阳,就可以調用 Class 對象的方法來獲得該對象的和該類的真實信息了。

  1. 獲取 Class 對應類的成員變量
    Field[] getDeclaredFields(); // 獲取 Class 對象對應類的所有屬性踏拜,與成員變量的訪問權限無關碎赢。
    Field[] getFields(); // 獲取 Class 對象對應類的所有 public 屬性。
    Field getDeclaredField(String name); // 獲取 Class 對象對應類的指定名稱的屬性速梗,與成員變量的訪問權限無關肮塞。
    Field getField(String name); // 獲取 Class 對象對應類的指定名稱的 public 屬性。

  2. 獲取 Class 對應類的方法
    Method[] getDeclaredMethods(); // 獲取 Class 對象對應類的所有聲明方法姻锁,于方法的訪問權限無關枕赵。
    Method[] getMethods(); // 獲取 Class 對象對應類的所有 public 方法,包括父類的方法位隶。
    Method getMethod(String name, Class<?>...parameterTypes); // 返回此 Class 對象對應類的拷窜、帶指定形參列表的 public 方法。
    Method getDeclaredMethod(String name, Class<?>...parameterTypes); // 返回此 Class 對象對應類的涧黄、帶指定形參列表的方法篮昧,與方法的訪問權限無關。

  3. 獲取 Class 對應類的構造函數(shù)
    Constructor<?>[] getDeclaredConstructors(); // 獲取 Class 對象對應類的所有聲明構造函數(shù)笋妥,于構造函數(shù)的訪問權限無關懊昨。
    Constructor<?>[] getConstructors(); // 獲取 Class 對象對應類的所有 public 構造函數(shù)。
    Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes); // 返回此 Class 對象對應類的春宣、帶指定形參列表的構造函數(shù)酵颁,與構造函數(shù)的訪問權限無關。
    Constructor<T> getConstructor(Class<?>...parameterTypes); // 返回此 Class 對象對應類的月帝、帶指定形參列表的 public 構造函數(shù)躏惋。

  4. 獲取 Class 對應類的 Annotation(注釋)
    <A extends Annotation>A getAnnotation(Class<A> annotationClass); // 嘗試獲取該 Class 對對象對應類存在的、指定類型的 Annotation嫁赏;如果該類型的注解不存在,則返回 null油挥。
    <A extends Annotation>A getDeclaredAnnotation(Class<A> annotationClass); // 這是Java8新增的方法潦蝇,該方法嘗試獲取直接修飾該 Class 對象對應類款熬、指定類型的Annotation;如果該類型的注解不存在攘乒,則返回 null贤牛。
    Annotation[] getAnnotations(); // 返回修飾該 Class 對象對應類存在的所有Annotation。
    Annotation[] getDeclaredAnnotations(); // 返回直接修飾該 Class 對應類的所有Annotation则酝。
    <A extends Annotation>A[] getAnnotationsByType(Class<A> annotationClass); // 獲取修飾該類的殉簸、指定類型的多個Annotation。
    <A extends Annotation>A[] getDeclaredAnnotationsByType(Class<A> annotationClass); // 獲取直接修飾該類的沽讹、指定類型的多個Annotation般卑。

  5. 獲取 Class 對應類的內部類
    Class<?>[] getDeclaredClasses(); // 返回該 Class 對象對應類包含的全部內部類。

  6. 獲取 Class 對應類的外部類
    Class<?> getDeclaringClass(); // 返回該 Class 對象對應類所在的外部類爽雄。

  7. 獲取 Class 對應類所實現(xiàn)的接口
    Class<?>[] getInterfaces();

  8. 獲取 Class 對應類所繼承的父類
    Class<? super T> getSuperClass();

  9. 獲取 Class 對應類的修飾符蝠检、所在包、類名等基本信息
    int getModifiers(); // 返回此類或接口的所有修飾符挚瘟。修飾符由 public叹谁、protected、private乘盖、final焰檩、static、abstract 等對應的常量組成订框,返回的整數(shù)應使用 Modifier 工具類的方法來解碼析苫,才可以獲取真實的修飾符。
    Package getPackage() // 獲取該類的包布蔗。
    String getName() // 以字符串的形式返回此 Class 對象所表示的類的名稱藤违。
    String getSimpleName() // 以字符串的形式返回此 Class 對象所表示的類的簡稱。

  10. 判斷該類是否為接口纵揍、枚舉顿乒、注解類型等
    boolean isAnnotation() // 返此 Class 對象是否是一個注解類型(由@interface定義)。
    boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) // 判斷此 Class 對象是否使用了Annotation修飾泽谨。
    boolean isAnonymousClass() // 此 Class 對象是否是一個匿名類璧榄。
    boolean isArray() //此 Class 對象是否是一個數(shù)組。
    boolean isEnum() // 此 Class 對象是否是一個枚舉(由 enum 關鍵字定義)吧雹。
    boolean isInterface() // 此 Class 對象是否是一個接口(由 interface 關鍵字定義)骨杂。
    boolean isInstance(Object obj) // 判斷 obj 是否是此 Class 對象的實例。該方法完全可以替代 instanceof 操作符雄卷。

3搓蚪、Java 8 新增的方法——參數(shù)反射

Java 8 在java.lang.reflect包下新增了一個 Executable 抽象基類,該對象代表可執(zhí)行的類成員丁鹉,該類派生了 Constructor妒潭、Method 兩個子類悴能。

Executable 基類提供了大量的方法來獲取修飾該方法或構造器的注解信息;還提供了
isVarArgs() // 用于判斷該方法或構造器是否包含數(shù)量可變的形參雳灾。
getModifiers() // 獲取該方法或構造器的修飾符漠酿。

此外,Executable 提供了如下兩個方法:
int ParamenterCount() // 獲取該構造器或方法的形參個數(shù)谎亩。
Paramenter[] getParamenters() // 獲取該構造器或方法的所有形參炒嘲。

上面的第二個方法返回了一個 Paramenter[] 數(shù)組,Paramenter也是 Java 8 新增的API匈庭,每個 Paramenter 對象代表方法或構造器的一個參數(shù)夫凸。

Paramenter 提供了大量方法來獲取聲明該參數(shù)的泛型信息,還提供了如下常用的方法來獲取參數(shù)信息:
getModifiers() // 獲取修飾該形參的修飾符嚎花。
String getName() // 獲取形參名寸痢。
Type getParamenterizedType() // 獲取帶泛型的形參類型。
Class<?> getType() // 獲取形參類型紊选。
boolean isNamePresent() // 返回該類的 class 文件中是否包含了方法的形參名信息啼止。
boolean isVarArgs() // 用于判斷該參數(shù)是否為個數(shù)可變的形參。

三兵罢、使用反射生成并操作對象

Class 對象可以獲得該類里的方法(由 Method 對象表示)献烦、構造器(由 Constructor 對象表示)、成員變量(由 Field 對象表示)卖词,這三個類都位于 java.lang.reflect 包下巩那。

程序可以通過 Method 對象來執(zhí)行對應的方法,
通過 Constructor 對象來調用對應的構造器創(chuàng)建實例此蜈,
通過 Field 對象直接訪問并修改對象的成員變量值即横。

1、創(chuàng)建對象

通過反射來生成對象的兩種方式:

  1. 使用 Class 對象的 newInstance() 方法來創(chuàng)建該 Class 對象對應類的實例裆赵。
    要求:Class 對象的對應類要有默認構造器东囚,而執(zhí)行 newInstance() 方法實際上是利用默認構造器來創(chuàng)建該類的實例。

  2. 先使用 Class 對象獲取指定的 Constructor 對象战授,再調用 Constructor 對象的 newInstance() 方法來創(chuàng)建該 Class 對象對應類的實例页藻。
    這種方式可以選擇使用指定的構造器來創(chuàng)建實例。

通過第一種方式來創(chuàng)建對象是比較常見的情形植兰,在很多的 JavaEE 框架中都需要根據(jù)配置文件信息來創(chuàng)建Java對象份帐。從配置文件中讀取的只是某個類的字符串類名,程序需要根據(jù)該字符串來創(chuàng)建對應的實例楣导,就必須使用到反射废境。

先建一個配置文件,obj.properties

a=java.util.Date
b=javax.swing.JFrame
/**
 * 功能:實現(xiàn)一個簡單的對象處
 * 思路:該對象池會根據(jù)配置文件讀取 key-value 對,然后創(chuàng)建這些對象噩凹,
 *       并將這些對象放入到一個 HashMap 中朦促。
 * @author Administrator
 *
 */
public class ObjectPoolFactory {
    
    // 定義一個對象池,<對象名栓始,實際對象>
    private Map<String, Object> objectPool = new HashMap<>();
    
    /**
     * 創(chuàng)建對象
     * @param className 字符串類名
     * @return 返回對應的 Java 對象
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private Object createObject(String className) 
            throws ClassNotFoundException
            , InstantiationException, IllegalAccessException {
        
        // 獲取對應的 Class 對象
        Class<?> clazz = Class.forName(className);
        // 使用對應類的默認構造器創(chuàng)建實例
        return clazz.newInstance();
    }
    
    /**
     * 根據(jù)指定文件來初始化對象池
     * @param fileName 配置文件名
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public void initPool(String fileName)
            throws ClassNotFoundException
            , InstantiationException, IllegalAccessException {
        
        try(FileInputStream fis = new FileInputStream(fileName)) {
            
            Properties props = new Properties();
            props.load(fis);
            for (String name : props.stringPropertyNames()) {
                objectPool.put(name, createObject(props.getProperty(name)));
            }
        } catch (IOException e) {
            System.out.println("讀取" + fileName + "異常!");
        }
    }
    
    /**
     * 根據(jù)指定的 name 值來獲取對應的對象
     * @param name 
     * @return
     */
    public Object getObject(String name) {
        return objectPool.get(name);
    }
}

測試文件:

public class Test {

    public static void main(String[] args)
            throws ClassNotFoundException
            , InstantiationException, IllegalAccessException {
        
        ObjectPoolFactory opf = new ObjectPoolFactory();
        opf.initPool("obj.properties");
        System.out.println(opf.getObject("a"));
        System.out.println(opf.getObject("b"));
        
    }

}

運行的結果:

這種使用配置文件來配置對象血当,然后由程序根據(jù)配置文件來創(chuàng)建對象的方式非常有用幻赚,如 Spring 框架就采用這種方式大大簡化了 JavaEE 應用的開發(fā),當然臊旭,Spring采用的是 XML 配置文件——因為 XML 配置文件能配置的信息更加的豐富落恼。

第二種方式,利用指定的構造器來創(chuàng)建 Java 對象离熏。

  1. 獲取該類的 Class 對象佳谦。
  2. 利用 Class 對象的 getConstrustor() 方法來獲取指定的構造器。
  3. 調用 Construstor 的 newInstance() 方法來創(chuàng)建 Java 對象滋戳。
public class CreateObject {
    
    public static void main(String[] args) throws Exception {
        
        Class<?> jframeClass = Class.forName("javax.swing.JFrame");
        Constructor<?> ctor = jframeClass.getConstructor(String.class);
        Object obj = ctor.newInstance("測試窗口");
        
        System.out.println(obj);
        
    }
    
}

實際上钻蔑,只有當程序需要動態(tài)創(chuàng)建某個類的對象時才會考慮使用反射,通常在開發(fā)通用性比較廣的框架奸鸯、基礎平臺時可能會大量使用反射咪笑。

2、調用方法

當獲得某個類對應的 Class 對象后娄涩,就可以通過該 Class 對象的方法:
getMethods() // 獲取全部方法窗怒。
getMethod() // 獲取指定方法。
這兩個方法的返回值是 Method 數(shù)組蓄拣,或者 Method 對象扬虚。

每個 Method 對象對應一個方法,獲得 Method 對象后球恤,程序就可通過該 Method 來調用它對應的方法辜昵。在 Method 里包含一個 invoke() 方法:
Object invoke(Object obj, Object... args) // 該方法中的 obj 是執(zhí)行該方法的主調,后面的 args 是執(zhí)行該方法時傳入該方法的實參碎捺。

// 生成新的對象:用newInstance()方法
Object obj = class1.newInstance();
// 首先需要獲得與該方法對應的Method對象
Method method = class1.getMethod("setAge", int.class);
// 調用指定的函數(shù)并傳遞參數(shù)
method.invoke(obj, 28);

當通過Method的invoke()方法來調用對應的方法時路鹰,Java會要求程序必須有調用該方法的權限。如果程序確實需要調用某個對象的 private 方法收厨,則可以先調用以下方法:
setAccessible(boolean flag) // 值為true晋柱,指示該 Method 在使用時應該取消Java語言的訪問權限檢查;值為false诵叁,則知識該Method在使用時要實施Java語言的訪問權限檢查雁竞。

Method 、 Constructor 、Field 都可以調用該方法碑诉,從而實現(xiàn)通過反射來調用 private 方法彪腔、private 構造器、private 成員變量进栽。

3德挣、訪問成員變量值

通過Class對象的:
getFields() // 獲取全部成員變量,返回 Field數(shù)組或對象快毛。
getField() // 獲得指定成員變量格嗅,返回 Field數(shù)組或對象。

Field 提供了兩組方法來讀取或設置成員變量的值:
getXXX(Object obj) // 獲取obj對象的該成員變量的值唠帝。
setXXX(Object obj, XXX val) // 將 obj 對象的該成員變量設置成val值屯掖。

//生成新的對象:用newInstance()方法 
Object obj = class1.newInstance();
//獲取age成員變量
Field field = class1.getField("age");
//將obj對象的age的值設置為10
field.setInt(obj, 10);
//獲取obj對象的age的值
field.getInt(obj);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市襟衰,隨后出現(xiàn)的幾起案子贴铜,更是在濱河造成了極大的恐慌,老刑警劉巖瀑晒,帶你破解...
    沈念sama閱讀 221,331評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绍坝,死亡現(xiàn)場離奇詭異,居然都是意外死亡苔悦,警方通過查閱死者的電腦和手機陷嘴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來间坐,“玉大人灾挨,你說我怎么就攤上這事≈袼危” “怎么了劳澄?”我有些...
    開封第一講書人閱讀 167,755評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蜈七。 經(jīng)常有香客問我秒拔,道長,這世上最難降的妖魔是什么飒硅? 我笑而不...
    開封第一講書人閱讀 59,528評論 1 296
  • 正文 為了忘掉前任砂缩,我火速辦了婚禮,結果婚禮上三娩,老公的妹妹穿的比我還像新娘庵芭。我一直安慰自己,他們只是感情好雀监,可當我...
    茶點故事閱讀 68,526評論 6 397
  • 文/花漫 我一把揭開白布双吆。 她就那樣靜靜地躺著眨唬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪好乐。 梳的紋絲不亂的頭發(fā)上匾竿,一...
    開封第一講書人閱讀 52,166評論 1 308
  • 那天,我揣著相機與錄音蔚万,去河邊找鬼岭妖。 笑死,一個胖子當著我的面吹牛反璃,可吹牛的內容都是我干的区转。 我是一名探鬼主播,決...
    沈念sama閱讀 40,768評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼版扩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了侄泽?” 一聲冷哼從身側響起礁芦,我...
    開封第一講書人閱讀 39,664評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悼尾,沒想到半個月后柿扣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,205評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡闺魏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,290評論 3 340
  • 正文 我和宋清朗相戀三年未状,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片析桥。...
    茶點故事閱讀 40,435評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡司草,死狀恐怖,靈堂內的尸體忽然破棺而出泡仗,到底是詐尸還是另有隱情埋虹,我是刑警寧澤,帶...
    沈念sama閱讀 36,126評論 5 349
  • 正文 年R本政府宣布娩怎,位于F島的核電站搔课,受9級特大地震影響,放射性物質發(fā)生泄漏截亦。R本人自食惡果不足惜爬泥,卻給世界環(huán)境...
    茶點故事閱讀 41,804評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崩瓤。 院中可真熱鬧袍啡,春花似錦、人聲如沸却桶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至畴嘶,卻和暖如春蛋逾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背窗悯。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工区匣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒋院。 一個月前我還...
    沈念sama閱讀 48,818評論 3 376
  • 正文 我出身青樓亏钩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親欺旧。 傳聞我的和親對象是個殘疾皇子姑丑,可洞房花燭夜當晚...
    茶點故事閱讀 45,442評論 2 359

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法辞友,內部類的語法栅哀,繼承相關的語法,異常的語法称龙,線程的語...
    子非魚_t_閱讀 31,660評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理留拾,服務發(fā)現(xiàn),斷路器鲫尊,智...
    卡卡羅2017閱讀 134,693評論 18 139
  • 一痴柔、概述 Java反射機制定義 Java反射機制是在運行狀態(tài)中,對于任意一個類疫向,都能夠知道這個類中的所有屬性和方法...
    CoderZS閱讀 1,642評論 0 26
  • 你是不是感覺金錢永遠不夠咳蔚? 你是不是被世俗的要求和社會的競爭追趕著,感到身心疲憊搔驼? 你是不是想擺脫金錢帶來的匱乏感...
    瀞好如琳閱讀 3,539評論 0 0
  • @夏雨半支煙:買川投能源好過買國投電力屹篓,川投能源沒那么瘋狂擴張,資金充足匙奴,管理費用低堆巧。國投每年都會買火電站,賣火電...
    新興市場的小邏輯閱讀 113評論 0 0