Java基礎(chǔ)之反射機(jī)制詳解

寫在最前

很高興前一段的文章能給大家?guī)?lái)幫助浙于,相信大家都清楚基礎(chǔ)知識(shí)對(duì)于程序員來(lái)說(shuō)是解決問(wèn)題的根基稻轨,筆者的系列文章將從英文定義政钟、思路分析缚去、實(shí)例應(yīng)用一步一步對(duì)重要的基礎(chǔ)知識(shí)進(jìn)行講解潮秘,沒(méi)關(guān)注的童鞋趕緊點(diǎn)一波關(guān)注,跟筆者一起鞏固你們的基礎(chǔ)吧R捉帷枕荞!

附前文地址:十個(gè)不可忽視的Java基礎(chǔ)知識(shí)

上次的文章中提到了Java的反射機(jī)制,但有讀者私下反映講解并不透徹搞动,仍然屬于懵圈狀態(tài)躏精,因此本文將對(duì)其進(jìn)行詳解。

RTTI和反射鹦肿?

這是一個(gè)經(jīng)常被混淆的問(wèn)題矗烛。RTTI or Reflection? 相信看過(guò)《Thinking in Java》的同學(xué)都有這么個(gè)疑惑,在Type Information這一章中有這么一段話箩溃。

Java allows you to discover information about objects and classes at run time. This takes two forms: "traditional" RTTI, which assumes that you have all the types available at compile time, and the reflection mechanism, which allows you to discover and use class information solely at run time.

這其中瞭吃,RTTI(Runtime Type Information)是C++ 中的一個(gè)概念,B大明顯先學(xué)的C++,在這本書之前也是先出版過(guò)《Thinking in C++》涣旨,因此可能對(duì)這個(gè)名詞有深厚的感情虱而?總之,千萬(wàn)不要嘗試去區(qū)分RTTI和反射(Reflection)的區(qū)別开泽,這兩者就像老人和男人(來(lái)自某知乎網(wǎng)友貼切的舉例),分開(kāi)理解是很蠢的魁瞪。

這里多說(shuō)一句穆律,諸如TIJ這種神書,也并不是適合每一個(gè)人去理解知識(shí)的导俘,在有些篇幅中峦耘,如果你發(fā)現(xiàn)自己卡住了,一定要多看例子旅薄,多查資料辅髓,用自己從中悟出的道理去解釋泣崩,不要去和死定義糾纏。

反射的目的

很多人在熟練掌握本文所述內(nèi)容后仍不解RTTI的目的洛口,一般有兩個(gè)原因:一是代碼編寫量過(guò)小矫付,自己設(shè)計(jì)的經(jīng)驗(yàn)不足,二則是不具備動(dòng)態(tài)的設(shè)計(jì)思路第焰。我們先來(lái)看下面和個(gè)例子(出自TIJ):

import java.util.*;

abstract class Shape{
    void draw() {
        System.out.println(this + ".draw() ");
    }
    abstract public String toString();
}

class Circle extends Shape {
    public String toString() {
        return "Circle ";
    }
}

class Square extends Shape {
    public String toString() {
        return "Square ";
    }
}

class Triangle extends Shape {
    public String toString() {
        return "Triangle ";
    }
}

public class Shapes {
    public static void main(String[] args) {
        List<Shape> shapeList = Arrays.asList(
            new Circle(), new Square(), new Triangle()
            );
        for (Shape shape : shapeList) {
            shape.draw();
        }
    }
}

在上例中买优,我們創(chuàng)建了Shape類,其中Circle, Triangle, 以及Square是他的三個(gè)子類挺举。在主方法中我們創(chuàng)建了Shape的list杀赢,然后用匿名的方式創(chuàng)建三種子類的引用,不難看出湘纵,在這里他們都被強(qiáng)制向上轉(zhuǎn)換為了Shape脂崔。所以輸出的值為:

/* 
Output:
Circle.draw()
Square.draw()
Triangle.draw()
*/

由此可見(jiàn),我們通過(guò)熟悉多態(tài)(Polymorphism)梧喷,也就是在子類中重寫toString()方法砌左,實(shí)現(xiàn)了最基礎(chǔ)的在運(yùn)行中獲取類型信息。利用此種方法伤柄,可使你的代碼更易讀寫及維護(hù)绊困,設(shè)計(jì)也更容易被理解,執(zhí)行或修改适刀。但如果我們不只是需要draw()方法被執(zhí)行秤朗,而需要執(zhí)行一些并不適用于全部子類的方法,比如rotate()笔喉,旋轉(zhuǎn)取视,對(duì)于Circle來(lái)說(shuō)執(zhí)行沒(méi)有任何意義,那么我們就需要反射機(jī)制常挚,我們便可以使Shape調(diào)用屬于他子類獨(dú)有的方法作谭。

這里光靠定義說(shuō)明確實(shí)是很難理解,舉個(gè)通俗的例子吧奄毡。比如屏幕上有一大堆各種各樣的水果折欠,蘋果,梨吼过,香蕉锐秦。蘋果被點(diǎn)擊時(shí)候執(zhí)行爆炸方法,香蕉被點(diǎn)擊的時(shí)候執(zhí)行剝皮方法盗忱,梨被點(diǎn)擊的時(shí)候砍成兩半酱床。那么我不知道用戶點(diǎn)擊的是哪個(gè)水果,但我希望直接執(zhí)行這個(gè)水果的方法趟佃,這時(shí)候利用反射機(jī)制將會(huì)使得我們的代碼簡(jiǎn)單已讀且利于修改再開(kāi)發(fā)扇谣。

Class類型及對(duì)象的獲取方法

在運(yùn)行時(shí)(runtime)承載類型信息的Class對(duì)象昧捷,你的程序中任何一個(gè)類,在你編譯的時(shí)候罐寨,這個(gè)Class對(duì)象都會(huì)被創(chuàng)建靡挥。簡(jiǎn)單點(diǎn)說(shuō)就是編譯后出現(xiàn)的Name.class那個(gè)文件,Name是和你的類名相同的名字(創(chuàng)建這個(gè)對(duì)象的是JVM的一個(gè)子系統(tǒng)叫做"class loader")衩茸。Java中獲取Class類型的方法有如下三種:

  • .class

所有的引用數(shù)據(jù)類型我們都可以在類名后加上".class"來(lái)獲取Class類型芹血,這種方法是靜態(tài)的。這里要多說(shuō)一句的是楞慈,若是基本類型幔烛,我們同樣可以使用“封裝類.TYPE”的方法,比如"int.class"對(duì)應(yīng)的是"Integer.TYPE"囊蓝。

  • Class.forName()

通過(guò)一個(gè)類的String名來(lái)獲取Class類型饿悬,這種方法同上一樣為靜態(tài),且必須拋出ClassNotFoundException聚霜。

  • getClass()

這種方法是通過(guò)實(shí)例名來(lái)獲取Class類型狡恬,是動(dòng)態(tài)的,這點(diǎn)與前兩種不同蝎宇,注意區(qū)分弟劲。

綜合來(lái)看上述三種方法,我們用一個(gè)表格來(lái)簡(jiǎn)單來(lái)區(qū)分他們:

方法名 獲取形式
.class 獲取聲明時(shí)的類
getClass() 獲取運(yùn)行時(shí)的類
Class.forName() 通過(guò)類名來(lái)獲得類

在獲得類型之后姥芥,在后面加一個(gè)newInstance()方法兔乞,便能創(chuàng)建對(duì)象,例如:

Class cc = Class.forName("Daniel");

Object oo = cc.newInstance();
//這里調(diào)用的是關(guān)于cc的無(wú)參數(shù)構(gòu)造方法

類中屬性的獲取方法

有了Class對(duì)象后凉唐,我們當(dāng)然希望對(duì)其的方法和參數(shù)進(jìn)行執(zhí)行或修改庸追,此處我們最常見(jiàn)的便是getDeclaredFields()方法了。例如:

Class cc = Class.forName("Daniel");

Field[] field = cc.getDeclaredFields();  

需要注意的是台囱,類中又很多方法淡溯,所以我們應(yīng)該定義一個(gè)Field數(shù)組。在這里常用的方法我將列在下面的這個(gè)表格當(dāng)中簿训,需要用時(shí)我們只需套用特定的方法即可咱娶,理解起來(lái)非常容易。

方法 作用
getDeclaredMethods() 獲取所有的方法
getReturnType() 獲得方法的返回類型
getParameterTypes() 獲得方法的傳入?yún)?shù)類型
getDeclaredMethod("",.class,……) 獲得特定的方法
getDeclaredConstructors() 獲取所有的構(gòu)造方法
getDeclaredConstructor(.class,……) 獲取特定的構(gòu)造方法
getSuperclass() 獲取某類的父類
getInterfaces() 獲取某類實(shí)現(xiàn)的接口

用以上方法進(jìn)行反編譯强品,使我們的代碼變得更加靈活豺总,這也是OO類型編程的一個(gè)終極目標(biāo)。

說(shuō)到這里择懂,我們?cè)僖黄饋?lái)看一下上次文章中提到的來(lái)自CSDN上的例子:

import java.lang.reflect.Array;     
import java.lang.reflect.Constructor;     
import java.lang.reflect.Field;     
import java.lang.reflect.Method;     
    
    
/**   
 * <a  class='replace_word' title="Java 知識(shí)庫(kù)" target='_blank' style='color:#df3434; font-weight:bold;'>Java </a>Reflection Cookbook   
 *   
 * @author Michael Lee   
 * @since 2006-8-23   
 * @version 0.1a   
 */    
    
public class Reflection {     
    /**   
     * 得到某個(gè)對(duì)象的公共屬性   
     *   
     * @param owner, fieldName   
     * @return 該屬性對(duì)象   
     * @throws Exception   
     *   
     */    
    public Object getProperty(Object owner, String fieldName) throws Exception {     
        Class ownerClass = owner.getClass();     
    
        Field field = ownerClass.getField(fieldName);     
    
        Object property = field.get(owner);     
    
        return property;     
    }     
    
    /**   
     * 得到某類的靜態(tài)公共屬性   
     *   
     * @param className   類名   
     * @param fieldName   屬性名   
     * @return 該屬性對(duì)象   
     * @throws Exception   
     */    
    public Object getStaticProperty(String className, String fieldName)     
            throws Exception {     
        Class ownerClass = Class.forName(className);     
    
        Field field = ownerClass.getField(fieldName);     
    
        Object property = field.get(ownerClass);     
    
        return property;     
    }     
    
    
    /**   
     * 執(zhí)行某對(duì)象方法   
     *   
     * @param owner   
     *            對(duì)象   
     * @param methodName   
     *            方法名   
     * @param args   
     *            參數(shù)   
     * @return 方法返回值   
     * @throws Exception   
     */    
    public Object invokeMethod(Object owner, String methodName, Object[] args)     
            throws Exception {     
    
        Class ownerClass = owner.getClass();     
    
        Class[] argsClass = new Class[args.length];     
    
        for (int i = 0, j = args.length; i < j; i++) {     
            argsClass[i] = args[i].getClass();     
        }     
    
        Method method = ownerClass.getMethod(methodName, argsClass);     
    
        return method.invoke(owner, args);     
    }     
    
    
      /**   
     * 執(zhí)行某類的靜態(tài)方法   
     *   
     * @param className   
     *            類名   
     * @param methodName   
     *            方法名   
     * @param args   
     *            參數(shù)數(shù)組   
     * @return 執(zhí)行方法返回的結(jié)果   
     * @throws Exception   
     */    
    public Object invokeStaticMethod(String className, String methodName,     
            Object[] args) throws Exception {     
        Class ownerClass = Class.forName(className);     
    
        Class[] argsClass = new Class[args.length];     
    
        for (int i = 0, j = args.length; i < j; i++) {     
            argsClass[i] = args[i].getClass();     
        }     
    
        Method method = ownerClass.getMethod(methodName, argsClass);     
    
        return method.invoke(null, args);     
    }     
    
    
    
    /**   
     * 新建實(shí)例   
     *   
     * @param className   
     *            類名   
     * @param args   
     *            構(gòu)造函數(shù)的參數(shù)   
     * @return 新建的實(shí)例   
     * @throws Exception   
     */    
    public Object newInstance(String className, Object[] args) throws Exception {     
        Class newoneClass = Class.forName(className);     
    
        Class[] argsClass = new Class[args.length];     
    
        for (int i = 0, j = args.length; i < j; i++) {     
            argsClass[i] = args[i].getClass();     
        }     
    
        Constructor cons = newoneClass.getConstructor(argsClass);     
    
        return cons.newInstance(args);     
    
    }     
    
    
         
    /**   
     * 是不是某個(gè)類的實(shí)例   
     * @param obj 實(shí)例   
     * @param cls 類   
     * @return 如果 obj 是此類的實(shí)例,則返回 true   
     */    
    public boolean isInstance(Object obj, Class cls) {     
        return cls.isInstance(obj);     
    }     
         
    /**   
     * 得到數(shù)組中的某個(gè)元素   
     * @param array 數(shù)組   
     * @param index 索引   
     * @return 返回指定數(shù)組對(duì)象中索引組件的值   
     */    
    public Object getByArray(Object array, int index) {     
        return Array.get(array,index);     
    }     
}

再一次回看我們會(huì)發(fā)現(xiàn)另玖,以上不過(guò)只是將各種方法的應(yīng)用簡(jiǎn)單寫出困曙,并沒(méi)有過(guò)多的邏輯表伦。簡(jiǎn)單來(lái)說(shuō),這種類型的代碼對(duì)于我們來(lái)說(shuō)只是字典型的材料慷丽,在設(shè)計(jì)的過(guò)程中即查即用蹦哼,沒(méi)必要逐一去背誦。

結(jié)語(yǔ)

關(guān)于Java反射部分的知識(shí)要糊,本文中也涉及得差不多了纲熏。對(duì)于反射機(jī)制,比起了解語(yǔ)法锄俄,更重要的是要去理解它在設(shè)計(jì)模式中發(fā)揮的作用局劲,而設(shè)計(jì)模式是Java編程思想中相當(dāng)重要的一環(huán),有關(guān)內(nèi)容在后續(xù)的文章中可能還會(huì)提及奶赠,希望大家持續(xù)關(guān)注鱼填。

文章中若有錯(cuò)誤或不盡人意之處還請(qǐng)大家指出,歡迎大家私信作者的微博:LightningDC或直接在文章下方回復(fù)進(jìn)行交流毅戈。筆者還會(huì)繼續(xù)為大家更新苹丸,歡迎關(guān)注,如果文章對(duì)你有用苇经,就隨手點(diǎn)個(gè)贊吧哈哈赘理,碼字不易。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末扇单,一起剝皮案震驚了整個(gè)濱河市商模,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌令花,老刑警劉巖阻桅,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異兼都,居然都是意外死亡嫂沉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門扮碧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)趟章,“玉大人,你說(shuō)我怎么就攤上這事慎王◎就粒” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵赖淤,是天一觀的道長(zhǎng)蜀漆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)咱旱,這世上最難降的妖魔是什么确丢? 我笑而不...
    開(kāi)封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任绷耍,我火速辦了婚禮,結(jié)果婚禮上鲜侥,老公的妹妹穿的比我還像新娘褂始。我一直安慰自己,他們只是感情好描函,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布崎苗。 她就那樣靜靜地躺著,像睡著了一般舀寓。 火紅的嫁衣襯著肌膚如雪胆数。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天基公,我揣著相機(jī)與錄音幅慌,去河邊找鬼。 笑死轰豆,一個(gè)胖子當(dāng)著我的面吹牛胰伍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酸休,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼斑司!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起宿刮,我...
    開(kāi)封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎僵缺,沒(méi)想到半個(gè)月后胡桃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翠胰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年自脯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了之景。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡膏潮,死狀恐怖锻狗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情脚囊,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站我擂,受9級(jí)特大地震影響缓艳,放射性物質(zhì)發(fā)生泄漏校摩。R本人自食惡果不足惜阶淘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坤塞。 院中可真熱鬧澈蚌,春花似錦、人聲如沸宛瞄。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)杯活。三九已至,卻和暖如春轩猩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背均践。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彤委,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓车遂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親坡疼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,734評(píng)論 25 707
  • 1. Java中的多態(tài)性理解(注意與C++區(qū)分) Java中除了static方法和final方法(private方...
    小敏紙閱讀 1,438評(píng)論 0 19
  • 其實(shí)想要孩子學(xué)習(xí)好剪况,學(xué)校發(fā)的習(xí)題就夠孩子練得了教沾,一定要學(xué)會(huì)舉一反三译断。不然做再多的題目也沒(méi)有用,學(xué)校留的作業(yè)已經(jīng)夠孩...
    海倫不哭閱讀 175評(píng)論 0 0
  • 一直在下雨的北京堪唐,穿了一件毛衣還是有些陰冷该贾。昨天才返校,今天卻覺(jué)得已經(jīng)過(guò)了很久的樣子杨蛋。晚自習(xí),看著民法總論的筆記逞力,...
    Skirtz閱讀 196評(píng)論 3 2
  • 昨天老公的摯友Q大規(guī)模宴請(qǐng)賓客揩抡,以慶祝他老家新宅的喬遷,為他喜悅又為自己的淡定喜悅峦嗤。 不知何時(shí)起,已...
    水深流緩閱讀 192評(píng)論 1 0