JAVA中的反射

簡介:

反射是Java語言中一個(gè)比較重要的特性帚称,它允許對(duì)正在運(yùn)行的Java進(jìn)行觀測(cè)咧纠,甚至動(dòng)態(tài)修改程序蓬痒,即在運(yùn)行態(tài),對(duì)于任意一個(gè)類漆羔,都能夠知道這個(gè)類的所有屬性和方法梧奢;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性演痒。

反射API介紹

使用反射API的第一步是獲取Class對(duì)象

1.已知具體的類亲轨,通過類的class屬性獲取,對(duì)于基本類型來說鸟顺,
它們的包裝類型(wrapper classes)擁有一個(gè)名為“TYPE”的final靜態(tài)字段惦蚊,
指向該基本類型對(duì)應(yīng)的Class對(duì)象

2.已知某個(gè)類的實(shí)例,調(diào)用對(duì)象的getClass()方法獲取Class對(duì)象

3.已知一個(gè)類的全類名讯嫂,使用靜態(tài)方法Class.forName來獲取


例如
Integer.TYPE 指向 int.class蹦锋。對(duì)于數(shù)組類型來說,
可以使用類名 +“[ ].class”來訪問欧芽,如 int[ ].class莉掂。

除此之外,Class 類和 java.lang.reflect 包中還提供了許多返回 Class 對(duì)象的方法千扔。
例如憎妙,對(duì)于數(shù)組類的 Class 對(duì)象,調(diào)用 Class.getComponentType() 方法可以獲得數(shù)組元素的類型曲楚。

我們還可以利用自定義Classloader來加載我們的類厘唾,然后可以獲取到該類的Class對(duì)象。類似于下面的代碼龙誊,注意代碼可能會(huì)拋出異常

MyClassLoader classLoader = new MyClassLoader(workspace + "/src/me/mingshan");
Class<?> proxy0Class = classLoader.findClass("$Proxy0");

拿到Class對(duì)象后阅嘶,我們可以進(jìn)行很多操作,比如生成該類的實(shí)例载迄,訪問字段的值讯柔,調(diào)用方法等。

生成類的實(shí)例

通過類的Class對(duì)象可以生成類的實(shí)例护昧,有兩種方式

  • 調(diào)用的是無參數(shù)的構(gòu)造函數(shù)進(jìn)行實(shí)例化
    clazz.newInstance();
  • 可以選擇調(diào)用哪個(gè)構(gòu)造函數(shù)進(jìn)行實(shí)例化魂迄,獲取構(gòu)造器可以傳入?yún)?shù)來選擇
Constructor c = clazz.getConstructor();
Object obj = c.newInstance();

訪問類的成員

通過調(diào)用getFiles()/getgetConstructors()/getMethods()來訪問該類的成員。同時(shí)我們會(huì)發(fā)現(xiàn)有些方法會(huì)帶上Declared惋耙,這表示調(diào)用該方法不會(huì)返回父類的成員捣炬。
當(dāng)然我們也可以直接獲取類的某個(gè)成員熊昌,比如成員和方法。

// 獲取字段
Field field = clazz.getField("name");

// 獲取方法
Method method=clazz.getMethod("studyHard", new Class[]{String.class});

還有一點(diǎn)湿酸,我們可以獲取該類實(shí)現(xiàn)的接口

// 獲取該類所實(shí)現(xiàn)的所有接口
Class<?> interfaces[] = clazz.getInterfaces();

對(duì)類成員操作

當(dāng)獲取到類成員后婿屹,我們可以進(jìn)行下一步操作

  • 使用 Constructor/Field/Method.setAccessible(true)來繞開 Java 語言的訪問限制。
  • 使用 Constructor.newInstance(Object[])來生成該類的實(shí)例推溃。
  • 使用Field.get/set(Object)來訪問字段的值昂利。
  • 使用Method.invoke(Object, Object[])來調(diào)用方法。

獲取泛型

在Java中泛型有擦除機(jī)制铁坎,那么我們?cè)谶\(yùn)行時(shí)可不可以獲取泛型的具體類型呢蜂奸?答案是可以的。原因是Class類文件結(jié)構(gòu)中有一個(gè)叫Signature的屬性硬萍。它的作用是存儲(chǔ)一個(gè)方法在字節(jié)碼層面的特征簽名扩所。這個(gè)屬性中保存的參數(shù)參數(shù)類型并不是原生類型,而是包括了參數(shù)化類型的信息朴乖。

獲取成員變量的泛型信息

比如在一個(gè)類Test中祖屏,有一個(gè)成員變量list,如下:
private List<String> list;
現(xiàn)在我想直接想獲取List的泛型买羞,怎么獲取呢袁勺?實(shí)現(xiàn)代碼如下:

Type t = Test.class.getDeclaredField("list").getGenericType();  
if (ParameterizedType.class.isAssignableFrom(t.getClass())) {
    for (Type t1 : ((ParameterizedType) t).getActualTypeArguments()) {
        System.out.print(t1 + ",");
    }
}

由于可能會(huì)有多個(gè)泛型參數(shù),例如Map哩都,所以返回是一個(gè)數(shù)組。

獲取類的泛型信息

一個(gè)類是泛型類婉徘,在該類中我們可能需要獲取這個(gè)泛型到底是啥漠嵌,然后繼續(xù)進(jìn)行下面的邏輯,一個(gè)應(yīng)用是Hibernate動(dòng)態(tài)拼接HQL盖呼,需要知道泛型信息儒鹿。
那么如何操作?代碼如下:

/**
 * 通過反射獲取泛型實(shí)例
 */
public class Genericity<T> {

    @SuppressWarnings("rawtypes")
    protected Class clazz;

    @SuppressWarnings("unchecked")
    /**
     * 把泛型的參數(shù)提取出來的過程放入到構(gòu)造函數(shù)中寫几晤,因?yàn)?     * 當(dāng)子類創(chuàng)建對(duì)象的時(shí)候约炎,直接調(diào)用父類的構(gòu)造函數(shù)
     */
    public Genericity() {
        // 通過反射機(jī)制獲取子類傳遞過來的實(shí)體類的類型信息
        ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
        //得到t的實(shí)際類型
        clazz = (Class<T>) type.getActualTypeArguments()[0];
    }

    /**
     * 獲取指定實(shí)例的所有屬性名及對(duì)應(yīng)值的Map實(shí)例 
     * @param entity 實(shí)例
     * @return 字段名及對(duì)應(yīng)值的Map實(shí)例
     */
    protected Map<String, Object> getFieldValueMap(T entity) {
        // key是屬性名,value是對(duì)應(yīng)值
        Map<String, Object> fieldValueMap = new HashMap<String, Object>();

        // 獲取當(dāng)前加載的實(shí)體類中所有屬性
        Field[] fields = this.clazz.getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            Field f = fields[i];
            // 屬性名 
            String key = f.getName();
            //屬性值 
            Object value = null; 
            // 忽略序列化版本ID號(hào)
            if (! "serialVersionUID".equals(key)) {
                // 取消Java語言訪問檢查
                f.setAccessible(true);
                try {
                    value =f.get(entity);
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                fieldValueMap.put(key, value);
            }
        }
        return fieldValueMap;
    }
}

在上面的代碼中蟹瘾,this.getClass().getGenericSuperclass()返回表示此 Class所表示的實(shí)體(類圾浅、接口、基本類型或 void)的直接超類的Type憾朴,然后將其轉(zhuǎn)換ParameterizedType狸捕。
getActualTypeArguments()返回表示此類型實(shí)際類型參數(shù)的 Type 對(duì)象的數(shù)組。

反射Array相關(guān)API

reflect.Array類位于java.lang.reflect包下,它是個(gè)反射工具包众雷,全是靜態(tài)方法灸拍。我們可以利用這個(gè)類來對(duì)數(shù)組進(jìn)行操作做祝。

調(diào)用Class.getComponentType()獲取數(shù)組元素的類型,代碼如下:

int[] arr = {1,2,3,4,5};
Class<?> c = arr.getClass().getComponentType();

假設(shè)我們需要獲取數(shù)組長度鸡岗,用Array的靜態(tài)方法獲然旎薄:

int len = Array.getLength(arr);

當(dāng)然我們可以用Array類來創(chuàng)建數(shù)組,向數(shù)組添加元素轩性,修改元素等声登,具體可以參考官方API。
最后推薦一個(gè)反射工具類炮姨,可以參考參考捌刮。

參考原文:https://mingshan.fun/2018/08/19/reflect/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市舒岸,隨后出現(xiàn)的幾起案子绅作,更是在濱河造成了極大的恐慌,老刑警劉巖蛾派,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俄认,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡洪乍,警方通過查閱死者的電腦和手機(jī)眯杏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來壳澳,“玉大人岂贩,你說我怎么就攤上這事∠锊ǎ” “怎么了萎津?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抹镊。 經(jīng)常有香客問我锉屈,道長,這世上最難降的妖魔是什么垮耳? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任颈渊,我火速辦了婚禮,結(jié)果婚禮上终佛,老公的妹妹穿的比我還像新娘俊嗽。我一直安慰自己,他們只是感情好铃彰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布乌询。 她就那樣靜靜地躺著,像睡著了一般豌研。 火紅的嫁衣襯著肌膚如雪妹田。 梳的紋絲不亂的頭發(fā)上唬党,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音鬼佣,去河邊找鬼驶拱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛晶衷,可吹牛的內(nèi)容都是我干的蓝纲。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼晌纫,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼税迷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锹漱,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤箭养,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后哥牍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毕泌,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年嗅辣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撼泛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澡谭,死狀恐怖愿题,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛙奖,我是刑警寧澤潘酗,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站外永,受9級(jí)特大地震影響崎脉,放射性物質(zhì)發(fā)生泄漏拧咳。R本人自食惡果不足惜伯顶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骆膝。 院中可真熱鬧祭衩,春花似錦、人聲如沸阅签。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽政钟。三九已至路克,卻和暖如春樟结,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背精算。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國打工瓢宦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人灰羽。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓驮履,卻偏偏與公主長得像,于是被迫代替她去往敵國和親廉嚼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子玫镐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 主要介紹以下幾方面內(nèi)容 理解 Class 類理解 Java 的類加載機(jī)制學(xué)會(huì)使用 ClassLoader 進(jìn)行類加...
    dinel閱讀 669評(píng)論 0 1
  • 引言:java的高級(jí)特性-反射一直是困擾自己的一個(gè)很大問題,今天專門花了半天再將java中的反射看了一遍怠噪,下面簡單...
    cp_insist閱讀 2,318評(píng)論 1 6
  • Class類的使用 Java中,靜態(tài)成員捂寿、普通數(shù)據(jù)類型不是對(duì)象口四,靜態(tài)成員是屬于類的,而不是屬于某個(gè)對(duì)象的 類是對(duì)象...
    紫軒丶閱讀 2,934評(píng)論 0 1
  • 概述 Java 反射是可以讓我們?cè)谶\(yùn)行時(shí)獲取類的方法秦陋、屬性蔓彩、父類、接口等類的內(nèi)部信息的機(jī)制驳概。也就是說赤嚼,反射本質(zhì)上是...
    absfree閱讀 6,239評(píng)論 3 15
  • 今天天氣很好
    Qiu_2bc9閱讀 168評(píng)論 0 0