java反射機(jī)制

基本概念

java的反射機(jī)制是動態(tài)獲取類的信息以及動態(tài)調(diào)用對象的方法翩腐。這種機(jī)制允許程序在運(yùn)行時通過reflection apis獲取一個已知名稱的class的內(nèi)部信息。包括:modifiers(如public余蟹,static等),superclass(如Object),實現(xiàn)了的Interfaces(如Serializable),也包括其fields和methods的所有信息验辞,并可以在運(yùn)行時動態(tài)改變fields或調(diào)用methods。

  • 主要功能
    • 在運(yùn)行時判斷任意一個對象所屬的類昂拂。
    • 在運(yùn)行時構(gòu)造任意一個類的對象受神。
    • 在運(yùn)行時判斷任意一個類所具有的成員變量和方法。
    • 在運(yùn)行時調(diào)用任意一個對象的方法格侯。

Java Reflection API簡介

在JDK中鼻听,主要由以下類來實現(xiàn)Java反射機(jī)制,這些類(除了第一個)都位于java.lang.reflect包中联四。

  • Class類:代表一個類撑碴,位于java.lang包下。
  • Field類:代表類的成員變量(及類的屬性)朝墩。
  • Method:代表類的方法醉拓。
  • Constructor類:代表類的構(gòu)造方法。
  • Array類:提供了動態(tài)創(chuàng)建數(shù)組收苏,以及訪問數(shù)據(jù)的元素的靜態(tài)方法亿卤。

Class對象

要想使用放射,首先需要獲得帶操作的類所對應(yīng)的Class對象鹿霸。java中排吴,無論生成某個類的多少對象,這些對象都會對應(yīng)于同一個Class對象懦鼠。這個Class對象是有JVM生成的钻哩,通過它能夠獲取整個類的結(jié)構(gòu)。
獲取Class對象的3中方法

  1. 使用Class類的靜態(tài)方法
 Class<?> stringClass = Class.forName("java.lang.String");
  1. 使用類的.class方法
Class<?> stringClass2 = String.class;
  1. 使用對象的getClass方法
String str = "aa";
Class<?> stringClass3 = str.getClass();

getClass方法定義在Object類中肛冶,不是靜態(tài)方法街氢,聲明為final,不能被子類覆寫睦袖。

使用方法

  • 打印類所有方法信息
public static void main(String[] args) throws ClassNotFoundException {
 // 獲取class對象
 Class<?> classType = Class.forName("java.lang.String");
 // 返回class對象所對應(yīng)類或接口中聲明德爾所有方法的數(shù)組【包括私有方法】
 Method[] methods = classType.getDeclaredMethods();
 // 輸出所有方法的聲明
 for (Method method : methods) {
     System.out.println(method);
 }
}

通過反射調(diào)用方法

public class Reflect {
    public int add(int param1, int param2) {
        return param1 + param2;
    }

    public String echo(String message) {
        return "Hello " + message;
    }

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        // 正常的執(zhí)行手段
        Reflect reflect = new Reflect();
        System.out.println(reflect.add(1, 2));
        System.out.println(reflect.echo("suys"));
        System.out.println("================================");
        // 反射的方法
        // 獲取class對象
        Class<?> classType = Reflect.class;
        // 生成新的對象
        Reflect reflect1 = (Reflect) classType.newInstance();
        System.out.println(reflect1 instanceof Reflect); // 判斷是否是Reflect類的對象
        // 通過反射調(diào)用方法
        // 首先需要獲得與該方法對應(yīng)的Method對象珊肃,第一個參數(shù)是方法名,第二個參數(shù)是所需要的參數(shù)Class對象的數(shù)組
        Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});
        // 調(diào)用目標(biāo)方法
        Object result = addMethod.invoke(reflect1, new Object[]{1, 2});
        System.out.println(result); //此時返回的是Integer類型

        // 第二個方法調(diào)用采用類似的方法
    }
}

兩種用法

// 接口
public interface CarInterface {
    public String getName();
    public String getSpeed();
}
// 實現(xiàn)類1
public class SmallCar implements CarInterface {
    @Override
    public String getName() {
        return "small car";
    }
    @Override
    public String getSpeed() {
        return "100-200";
    }
}
// 實現(xiàn)類2
public class BigCar implements CarInterface{
    @Override
    public String getName() {
        return "big car";
    }
    @Override
    public String getSpeed() {
        return "10-50";
    }
}
// 測試類
public class Test {
    public static void main(String[] args) throws Exception {
        List<Class<?>> carInterfaces = Lists.newArrayList();
        carInterfaces.add(SmallCar.class);
        carInterfaces.add(BigCar.class);
        for(Class<?> carInterface : carInterfaces) {
            if (carInterface instanceof Class) {
                Object obj = carInterface.newInstance();
                System.out.println(carInterface.getName());
                Method method= carInterface.getMethod("getName");
                Object result = method.invoke(obj);
                System.out.println(result);
            }
        }
    }
    public static void main2(String[] args) throws Exception {
        List<CarInterface> carInterfaces = Lists.newArrayList();
        carInterfaces.add(new BigCar());
        carInterfaces.add(new SmallCar());
        for(CarInterface carInterface : carInterfaces) {
            if (carInterface instanceof CarInterface) {
                Class<?> cc = carInterface.getClass();
                System.out.println(cc.getName());
                Method method= cc.getMethod("getName");
                Object result = method.invoke(carInterface);
                System.out.println(result);
            }
        }
    }
}

生成對象

如果像拖過類的不帶參數(shù)的構(gòu)造方法來生成對象馅笙,有兩種方式:

  1. 先獲得Class對象伦乔,然后通過該Class對象的newInstance()方法直接生成
Class<?> classType = String.class;
Object obj = classType.newInstance;
  1. 先獲得Class對象,然后通過改對象獲得對應(yīng)的Constructor對象延蟹,在通過Constructor對象的newInstance()方法生成(其中Customer是一個自定義的類评矩,有一個無參數(shù)的構(gòu)造函數(shù),也有帶參數(shù)的構(gòu)造方法)
Class<?> classType = Customer.class;
Constructor con = classType.getConstructor(new Class[]{});
Object obj = con.newInstance(new Object[]{});
Object result = classType.getMethod("getName").invoke(obj);

如果想通過類的帶參數(shù)的構(gòu)造方法生成對象阱飘,只能使用上面的第二種方法斥杜。
可以看出調(diào)用構(gòu)造方法生成對象的方法和調(diào)用一般方法的類似,不同的是從Constructor對象時不需要指定名字沥匈,而獲取Method對象時需要指定名字蔗喂。

利用反射實現(xiàn)對象拷貝的例子

// customer類
public class Customer {
    private String name;
    private String email;
    public Customer() {
    }
    public Customer(String a, String b) {
        name = a;
        email = b;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
// 類copy以及測試方法
public Object copy(Object object) throws Exception {
    Class<?> classType = object.getClass();
    Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
    // 獲得對象的所有成員變量
    Field[] fields = classType.getDeclaredFields();
    for (Field field : fields) {
        // 獲取成員變量的名字
        String name = field.getName();
        // 獲取get和set方法
        String firstLetter = name.substring(0, 1).toUpperCase(); // 將屬性的首字母轉(zhuǎn)換為大寫
        String getMethodName = "get" + firstLetter + name.substring(1);
        String setMethodName = "set" + firstLetter + name.substring(1);
        // 獲取方法對象
        Method getMethod = classType.getMethod(getMethodName, new Class[]{});
        Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()}); //注意set方法需要傳入?yún)?shù)類型
        // 調(diào)用get方法獲取舊的對象的值
        Object value = getMethod.invoke(object, new Object[]{});
        // 調(diào)用set方法將這個值賦值到新的對象中去
        setMethod.invoke(objectCopy, new Object[]{value});
    }
    return objectCopy;
}
public static void main(String[] args) throws Exception {
    Customer customer = new Customer("Tom", "tom@163.com");
    Reflect reflect = new Reflect();
    Customer customer1 = (Customer)reflect.copy(customer);
    System.out.println(customer1.getEmail() + "," + customer1.getName());
}

反射與數(shù)組

java.lang.Array類提供了動態(tài)創(chuàng)建和訪問數(shù)組元素的各種靜態(tài)方法。
下面例子創(chuàng)建了一個長度為10的字符串?dāng)?shù)組高帖,接著把索引位置為5的元素設(shè)為Hello缰儿,然后在讀取索引位置為5的元素的值。

public static void main(String[] args) throws Exception {
    Class<?> classType = Class.forName("java.lang.String");
    // 生成數(shù)組散址,指定元素類型和數(shù)組長度
    Object array = Array.newInstance(classType, 10);
    Array.set(array, 5, "hello");
    System.out.println((String)Array.get(array, 5));
}

多維數(shù)組
首先區(qū)別一下下面兩者

System.out.println(Integer.TYPE); ==> int
System.out.println(Integer.class); ==>java.lang.Integer
public static void main(String[] args) {
    int[] dims = new int[]{5, 10, 15};
    // 注意區(qū)分以下兩種
    System.out.println(Integer.TYPE); // int
    System.out.println(Integer.class); // Integer
    // 創(chuàng)建一個三維數(shù)組乖阵,這個數(shù)組的三個維度分別是5,10,15
    Object array = Array.newInstance(Integer.TYPE, dims);
    // 可變參數(shù)宣赔,也可以這樣寫
    //Object array = Array.newInstance(Integer.TYPE, 5, 10, 15);
    System.out.println(array instanceof int[][][]);
    Class<?> classType = array.getClass().getComponentType(); //返回數(shù)組元素類型
    System.out.println(classType); // 三維數(shù)組的元素為二維數(shù)組,輸出:CLass[[
    // 獲得第一層的索引為3的數(shù)組瞪浸,返回的是一個二維數(shù)組
    Object arrayObject = Array.get(array, 3);
    Class<?> classType1 = arrayObject.getClass().getComponentType(); // 返回數(shù)組的元素類型
    System.out.println(classType1); // 二維數(shù)組的元素為一維數(shù)組儒将,輸出:class [
    // 此處返回的是一個一維數(shù)組
    arrayObject = Array.get(arrayObject, 5);
    Class<?> classType2 = arrayObject.getClass().getComponentType(); //返回數(shù)組元素類型
    System.out.println(classType2); // 一維數(shù)組的元素為int
    // 給一維數(shù)組下標(biāo)為10的位置設(shè)置值為37
    Array.setInt(arrayObject, 10, 37);
    int[][][] arrayCast = (int[][][]) array;
    System.out.println(arrayCast[3][5][10]);
}

利用反射調(diào)用私有方法、訪問私有屬性

利用反射对蒲,首先是Class對象的獲取钩蚊,之后是Method和Field的udixiang的獲取。
以Method為例蹈矮,可以看到getMethod()返回的是public的Method對象砰逻,而getDeclaredMethod()返回Method對象可以是非public的。
Field的方法同理泛鸟。
訪問私有屬性和方法蝠咆,在使用前要通過AccessibleObject類(Constructor、Field和Method類的基類)中的setAccesible()方法來抑制Java訪問權(quán)限的檢查谈况。
實例1.調(diào)用私有方法

// 有私有方法的類
public class PrivateClass {
    private String sayHello(String name) {
        return "Hello: " + name;
    }
}
// 利用反射機(jī)制訪問該方法
public class TestPrivate {
    public static void main(String[] args) throws Exception {
        PrivateClass privateClass = new PrivateClass();
        Class<?> classType = privateClass.getClass();
        // 獲取Method對象
        Method method = classType.getDeclaredMethod("sayHello", new Class[]{String.class});
        // 抑制Java的訪問控制檢查
        method.setAccessible(true);
        // 如果不加上面語句,報錯
        // Class com.suys.java.reflect.TestPrivate can not access a member of class com.suys.java.reflect.PrivateClass with modifiers "private"
        String str = (String)method.invoke(privateClass, new Object[]{"suys"});
        System.out.println(str);
    }
}

實例2.訪問私有屬性

// 有私有屬性的類
public class PrivateClass2 {
    private String name = "zhangsan";
    public String getName() {
        return name;
    }
}
//測試方法
public static void main(String[] args) throws Exception {
    PrivateClass2 privateClass2 = new PrivateClass2();
    Class<?> classType = privateClass2.getClass();
    Field field = classType.getDeclaredField("name");
    field.setAccessible(true);
    field.set(privateClass2, "lisi");
    System.out.println(privateClass2.getName());
}

Class類

java中的Object是所有類的繼承根源勺美,其中g(shù)etClass()方法返回一個Class Object。所有的類都有這個方法碑韵。
Class類十分特殊赡茸,它和其他類一樣繼承自O(shè)bject,其實體用以表達(dá)java程序運(yùn)行時的classes和interfaces祝闻,也用來表達(dá)enum占卧、array、primitive Java types(boolean,byte,char,short,int,long,float,double)以及關(guān)鍵詞void联喘。
當(dāng)一個class被加載华蜒,或當(dāng)加載器(class loader)的defineClass()被JVM調(diào)用,JVM便自動產(chǎn)生一個Class對象(Class object)豁遭。
如果想借由修改Java標(biāo)準(zhǔn)庫源碼來觀察Class對象的實際生成時機(jī)叭喜,例如在Class的constructor內(nèi)添加println,那是不可以的蓖谢,因為Class沒有public constructor捂蕴。
Class是Reflection的起源,針對任何類闪幽,唯有先為它產(chǎn)生一個Class object啥辨,接下來才能經(jīng)由后者喚起為數(shù)十多個的Reflection APIs。

參考:http://www.cnblogs.com/doudouxiaoye/p/5819813.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盯腌,一起剝皮案震驚了整個濱河市溉知,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖级乍,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舌劳,死亡現(xiàn)場離奇詭異,居然都是意外死亡卡者,警方通過查閱死者的電腦和手機(jī)蒿囤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門客们,熙熙樓的掌柜王于貴愁眉苦臉地迎上來崇决,“玉大人,你說我怎么就攤上這事底挫『闵担” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵建邓,是天一觀的道長盈厘。 經(jīng)常有香客問我,道長官边,這世上最難降的妖魔是什么沸手? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮注簿,結(jié)果婚禮上契吉,老公的妹妹穿的比我還像新娘。我一直安慰自己诡渴,他們只是感情好捐晶,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妄辩,像睡著了一般惑灵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上眼耀,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天英支,我揣著相機(jī)與錄音,去河邊找鬼哮伟。 笑死干花,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的澈吨。 我是一名探鬼主播把敢,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谅辣!你這毒婦竟也來了修赞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柏副,沒想到半個月后勾邦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡割择,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年眷篇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荔泳。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡蕉饼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玛歌,到底是詐尸還是另有隱情昧港,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布支子,位于F島的核電站创肥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏值朋。R本人自食惡果不足惜叹侄,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昨登。 院中可真熱鬧趾代,春花似錦、人聲如沸篙骡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糯俗。三九已至尿褪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間得湘,已是汗流浹背杖玲。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留淘正,地道東北人摆马。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像鸿吆,于是被迫代替她去往敵國和親囤采。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法惩淳,類相關(guān)的語法蕉毯,內(nèi)部類的語法乓搬,繼承相關(guān)的語法,異常的語法代虾,線程的語...
    子非魚_t_閱讀 31,581評論 18 399
  • 一进肯、概述 Java反射機(jī)制定義 Java反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個類棉磨,都能夠知道這個類中的所有屬性和方法...
    CoderZS閱讀 1,631評論 0 26
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理江掩,服務(wù)發(fā)現(xiàn),斷路器乘瓤,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • [toc] 反射機(jī)制: 允許程序在運(yùn)行時取得任何一個已知名稱的class的內(nèi)部信息环形,容許程序在運(yùn)行時加載、探知馅扣、使...
    卡路fly閱讀 2,554評論 2 14
  • 一個人不應(yīng)當(dāng)虛度一天的時光斟赚,他至少應(yīng)當(dāng)聽一曲好歌,讀一首好詩差油,看一副好畫。如果可能的話任洞,至少說幾句通達(dá)的話蓄喇。
    L風(fēng)閱讀 149評論 0 0