Java反射機(jī)制詳解

寫在前面的話:
很多人會(huì)說我直接new一個(gè)對象不就完了么,干嘛還用反射來獲取對象。因?yàn)閚ew屬于靜態(tài)編譯,而反射屬于動(dòng)態(tài)編譯,反射只有到運(yùn)行時(shí)他才會(huì)去獲得該對象的實(shí)例。從這點(diǎn)上可以看出反射的強(qiáng)大之處,我們接下來就來詳細(xì)介紹Java反射機(jī)制。

首先我們先來看幾種獲取class對象的方法。(鋪墊)####

  1. 所有的引用數(shù)據(jù)類型(類-類型)的類名、基本數(shù)據(jù)類型都可以通過.class方式獲取其Class對象(對于基本數(shù)據(jù)類型的封裝類還可以通過.TYPE 的方式獲取其Class對象,但要注意,TYPE實(shí)際上獲取的是封裝類對應(yīng)的基本類型的Class對象的引用,那么你可以判斷出int.class==Integer.TYPE 返回true,int.class==Integer.class返回false)。通過這種方式不會(huì)初始化靜態(tài)域。使用.class.TYPE的方式獲取Class對象叫做類的字面常量。這樣做不僅更簡單,而且更安全,因?yàn)樗诰幾g時(shí)就會(huì)受到檢查(因此不需要置于try語句塊中),并且它根除了對forName方法的引用,所以也更高效。
  2. Class的forName(String name)傳入一個(gè)類的完整類路徑也可以獲得Class對象,但由于使用的是字符串,必須強(qiáng)制轉(zhuǎn)換才可以獲取泛型的Class<T>的Class對象,并且你必須獲取這個(gè)方法可能拋出的ClassNotFoundException異常。這種方法可以初始化靜態(tài)域。
  3. 還可通過類的對象實(shí)例下的getClass()方法來獲取Class對象,即實(shí)例名.getClass()。

再來看看反射機(jī)制的定義。####

百科上給出的定義是:Java反射機(jī)制是指在運(yùn)行狀態(tài)中對于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對于任意一個(gè)對象,都能夠調(diào)用它的任意一個(gè)方法和屬性;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對象的方法的功能稱為java語言的反射機(jī)制。用一句話總結(jié)就是反射可以實(shí)現(xiàn)在運(yùn)行時(shí)可以知道任意一個(gè)類的屬性和方法。

使用反射機(jī)制有什么好處呢,哪些場合會(huì)用到呢?####

上面反射機(jī)制的定義還是很言簡意賅的。因?yàn)榉瓷淇梢栽谶\(yùn)行時(shí)加載、探知、使用完全未知的類,你只要給我一個(gè)路徑就可以了,我通過這個(gè)路徑拿到我需要的東西,我的操作與你無關(guān)了,靈活性更高了,類之間的耦合性也就降低了。

同時(shí)我們可以獲取到類的私有信息,破壞了類的封裝,使類變得不安全。使用反射還會(huì)降低程序性能。所以說使用反射優(yōu)點(diǎn)突出,缺點(diǎn)同樣突出。

反射應(yīng)用到的場合:

  1. 在運(yùn)行時(shí)判斷任意一個(gè)對象所屬的類。
Class aClass = Class.forName("com.Dan.Consumer");
System.out.print("?"+aClass.isInstance(new Consumer()));
// public boolean isInstance(Object obj)
// 當(dāng)該 Class 對象表示一個(gè)已聲明的類時(shí),若指定的 Object 參數(shù)是所表示類(或其任一子類)的一個(gè)實(shí)例,則此方法返回 true;否則返回 false。
  1. 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對象。
Class aClass1 = Class.forName("com.Dan.Consumer");
Object object = aClass1.newInstance();
// throws InstantiationException, IllegalAccessException
  1. 在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法。
Class aClass2 = Class.forName("com.Dan.Consumer");
Method[] methods = aClass2.getDeclaredMethods();
Field[] fields = aClass2.getDeclaredFields();
  1. 在運(yùn)行時(shí)調(diào)用任意一個(gè)對象的方法。
Class aClass3 = Class.forName("com.Dan.Consumer");
Method[] methods[]= aClass3.getDeclaredMethods();
Object object = aClass3.newInstance();
Object returnObject = methods.invoke(object,null);

簡單的說就是程序運(yùn)行時(shí),如果需要外部傳進(jìn)來一個(gè)對象,然后在程序中運(yùn)用這個(gè)對象,我們就可以通過配置文件中類的路徑,得到這個(gè)對象的所有信息,從而加以應(yīng)用。

Java的很多框架的底層都是用反射來實(shí)現(xiàn)的,比如Struts2,JDBC,SQLite...

那反射具體是怎么實(shí)現(xiàn)的呢?####

首先我們要知道Java里實(shí)現(xiàn)反射的那個(gè)類是java.lang.reflect。我們查看API在類摘要里主要有以下四個(gè)類:
① java.lang.reflect.AccessibleObject
② java.lang.reflect.Constructor<T>
③ java.lang.reflect.Field
④ java.lang.reflect.Method
其中AccessibleObject 是另外三個(gè)類的基類。我們接下來涉及的主要就是這幾個(gè)類。

在本文一開始就介紹了三種獲取class對象的方法,那么反射用到的是第2個(gè)方法。

  1. 獲取類的對象
Class<?> clazz  = Class.forName("com.Dan.Consumer");
Object object = clazz.newInstance();
  1. 獲取類的方法
public Method[] getDeclaredMethods()

返回 Method 對象的一個(gè)數(shù)組,這些對象反映此 Class 對象表示的類或接口聲明的所有方法,包括公共、保護(hù)、默認(rèn)(包)訪問和私有方法,但不包括繼承的方法

public Method getDeclaredMethod(String name,
                                Class<?>... parameterTypes)

返回一個(gè) Method 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法。

public Method[] getMethods()

返回一個(gè)包含某些 Method 對象的數(shù)組,這些對象反映此 Class 對象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共成員方法

public Method getMethod(String name,
                        Class<?>... parameterTypes)

返回一個(gè) Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。
調(diào)用方法(這里就不全貼了,可以參考下面總示例代碼):

Method method = clazz.getMethod("info", String.class, long.class);//獲取方法
method.invoke(object, "隔壁老王",2017032009);//通過invoke調(diào)用該方法

invoke:如果底層方法是靜態(tài)的,或底層方法所需的形參數(shù)為 0,那么可以忽略指定的 obj 參數(shù),該參數(shù)可以為 null。

  1. 獲取成員變量信息
public Field[] getDeclaredFields()

返回 Field 對象的一個(gè)數(shù)組,這些對象反映此 Class 對象所表示的類或接口所聲明的所有字段。包括公共、保護(hù)、默認(rèn)(包)訪問和私有字段,但不包括繼承的字段

public Field getDeclaredField(String name)

返回一個(gè) Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段。

public Field[] getFields()

返回一個(gè)包含某些 Field 對象的數(shù)組,這些對象反映此 Class 對象所表示的類或接口的所有可訪問公共字段。如果該 Class 對象表示一個(gè)類,則此方法返回該類及其所有超類的公共字段。如果該 Class 對象表示一個(gè)接口,則此方法返回該接口及其所有超接口的公共字段。

public Field getField(String name)

返回一個(gè) Field 對象,它反映此 Class 對象所表示的類或接口的指定公共成員字段。
獲得成員變量:

Field aField = clazz.getDeclaredField("name"); //因?yàn)閚ame變量是private的,所以不能用getField方法
aField.setAccessible(true);//值為 true 則指示反射的對象在使用時(shí)應(yīng)該取消 Java 語言訪問檢查。值為 false 則指示反射的對象應(yīng)該實(shí)施 Java 語言訪問檢查。
aField.set(object,"二大爺");
Object obj = aField.get(object);
System.out.println(obj);
  1. 獲取構(gòu)造器
public Constructor<?>[] getDeclaredConstructors()
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
public Constructor<?>[] getConstructors()
public Constructor<T> getConstructor(Class<?>... parameterTypes)

構(gòu)造器和上面的方法、屬性都是大同小異的。需要注意的是,當(dāng)返回一個(gè)數(shù)組的時(shí)候,返回類型是Constructor<?>[]而不是Constructor<T>[],因?yàn)閺拇朔椒ǚ祷刂螅摂?shù)組可能被修改以保存不同類的 Constructor 對象。

代碼示例####

一個(gè)關(guān)于顧客的類Consumer:

public class Consumer {
    private long id;//私有的
    public String name;//共有的
    /*沒參數(shù)構(gòu)造體*/
    public Consumer() {
    }
    /*有參數(shù)構(gòu)造體*/
    public Consumer(long id, String name) {
        this.id = id;
        this.name = name;
    }
    /*getter setter*/
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    /*私有的無參buy方法*/
    private static void buy() {
        System.out.println("私有的無參buy方法");
    }
    /*共有的有參有返回值consume方法*/
    public String consume(String giftName) {
        System.out.println("買了一件禮物: " + giftName);
        return giftName;
    }
}

TestReflect類通過反射得到Consumer類的信息

public class TestReflect {
    public static void getProperty() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        try {
            /* 獲取對象類型 */
         Class<?> clazz  = Class.forName("com.Dan.Consumer");
         Object object = clazz.newInstance();
            /* 獲取到所有屬性 */
            Field[] field = clazz.getDeclaredFields();
            for (Field f:field) {
                //String fieldName = f.getName();// 取到屬性名字
                //System.out.println(fieldName);
                System.out.println(f);
            }
            /* 獲取到所有的方法,包括私有的,但不包括父類的 */
            Method[] methods = clazz.getDeclaredMethods();
            for (Method m:methods){
                //String methodName = m.getName();
                //System.out.println(methodName);
                System.out.println(m);
            }
            /* 所有的構(gòu)造體 */
            Constructor[] constructors = clazz.getDeclaredConstructors();
            for (Constructor c:constructors){
                System.out.println(c);
            }
            // 調(diào)用方法
            Method method = clazz.getMethod("info", String.class, long.class);//獲取方法
            method.invoke(object, "隔壁老王",2017032009);
            //得到屬性
            Field aField = clazz.getDeclaredField("name"); //因?yàn)閚ame變量是private的,所以不能用getField方法
            aField.setAccessible(true);
            aField.set(object,"二大爺");
            Object obj = aField.get(object);
            System.out.println(obj);
            // 得到構(gòu)造器
            Constructor constructor = clazz.getDeclaredConstructor(long.class, String.class);
            constructor.newInstance(2016040221, "王小二");
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException,
            InstantiationException, IllegalAccessException, NoSuchFieldException {
        getProperty();
    }
}

// private long com.Dan.Consumer.id
// public java.lang.String com.Dan.Consumer.name
// public java.lang.String com.Dan.Consumer.getName()
// public void com.Dan.Consumer.setName(java.lang.String)
// public long com.Dan.Consumer.getId()
// private static void com.Dan.Consumer.buy()
// public void com.Dan.Consumer.setId(long)
// public void com.Dan.Consumer.info(java.lang.String,long)
// public com.Dan.Consumer()
// public com.Dan.Consumer(long,java.lang.String)
// 我是:隔壁老王,會(huì)員編號: 2017032009。
// 二大爺

寫完嘍!ㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏ


知識重在總結(jié)和梳理,只有不斷地去學(xué)習(xí)并運(yùn)用,才能化為自己的東西。當(dāng)你能為別人講明白的時(shí)候,說明自己已經(jīng)掌握了。

歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處!

如果有錯(cuò)誤的地方,或者有您的見解,還請不嗇賜教!

喜歡的話,麻煩點(diǎn)個(gè)贊!

參考:https://zhidao.baidu.com/question/411858703.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,067評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,084評論 2 375

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,799評論 18 139
  • 本篇文章繼續(xù)介紹Java反射機(jī)制,不同的是側(cè)重于介紹動(dòng)態(tài)代理。動(dòng)態(tài)代理是代理模式中的一種,是通過Java反射機(jī)制來...
    Ruheng閱讀 3,280評論 1 44
  • 本篇文章主要講解反射中泛型的一些知識點(diǎn)。主要分為兩個(gè)部分:第一部分在反射中Class類使用泛型;第二部分通過反射來...
    Ruheng閱讀 1,729評論 0 22
  • 真痛,真難受 認(rèn)識那么久,發(fā)現(xiàn)自己還是不了解你,很多東西都猜測揣度不出 或許我根本不了解你吧 我知道你對我有期待,...
    阿立立哥閱讀 176評論 0 0
  • 大數(shù)據(jù)時(shí)代,電商企業(yè)平臺都有一個(gè)困擾點(diǎn)沒有得到很好的解決:用戶隱私不容侵犯,而為了提供更好的用戶體驗(yàn)服務(wù),必須獲取...
    Redline閱讀 917評論 0 0