中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

JAVA中的糕富帥技術(shù)——反射(一)

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用


 今天就來談?wù)劮瓷錂C(jī)制;學(xué)過JAVA的人不一定懂得反射,但是一定聽說過反射,不過也僅僅是聽說過而已;因?yàn)榉瓷溆玫牡胤揭膊粫?huì)那么多,但是反射用的妙經(jīng)常會(huì)解決我們撓破頭皮的大問題。至于諸如為什么叫做反射、而不叫做正射倒射此類的歷史問題,還是交給歷史學(xué)家去研究吧。。。

反射的基石

  在談反射之前,我們應(yīng)該先了解下類的概念來引入。類是一種抽象的概念,舉個(gè)例子“我爸是李剛我爸李雙江”,從這句話中我們發(fā)現(xiàn)有李剛、李雙江這兩個(gè)人,我們來抽象它們的特點(diǎn),我們發(fā)現(xiàn)它們都像人。沒錯(cuò),那么我們就可以將人作為它們的一個(gè)抽象,反過來說李剛和李雙江就是人的一個(gè)具體實(shí)例;所以我們可以用一個(gè)Person類代表人來表示這種抽象。既然理解了類的概念,那些年那些陪我們度過日日夜夜的java類們,我們是不是也應(yīng)該抽象出一個(gè)類來證明一下他們,沒錯(cuò),那就是Class了!

  Class就是java類的抽象,它抽象出了java的共性,如類的名字、類的構(gòu)造方法、類的成員變量、類的老爸、類的方法等等等。既然這么說,那么我們通過這個(gè)Class,我們就可以得到這個(gè)類的方方面面的信息、興許還能比查戶口還詳細(xì)呢。我們創(chuàng)建出的每一個(gè)類,例如person類,說到底也就是我們實(shí)例化了一個(gè)Class的實(shí)例,來保存person類的名字、變量、方法這些信息,在內(nèi)存中表示就是保存了person類的字節(jié)碼,如果你理解了這些并且接受了我的看法,那么咱們有共同語(yǔ)言,可以繼續(xù)往下說。

  既然person類有擁有自己的字節(jié)碼,那么我們可以獲取到這個(gè)字節(jié)碼嗎?答案是肯定的,而且還不止一種方法。參見代碼:


public static void main(String[] args) throws Exception {
  //第一種方法,直接通過Person類來獲取字節(jié)碼   Class cls1 = Person.class;
  //第二種方法,通過類的實(shí)例來獲取Person類的字節(jié)碼   Person person = new Person();
  Class cls2 = person.getClass();
  //第三種方法,調(diào)用Class類的靜態(tài)方法來獲取對(duì)應(yīng)類的字節(jié)碼,該方法會(huì)拋出異常   Class cls3 = Class.forName("Person");
}

  從代碼中看,我們可以斷定:Person類的字節(jié)碼就是Class的具體實(shí)例;我們也可以猜到,至于類的字節(jié)碼有包含什么東東,大家盡管猜吧,后面我會(huì)慢慢講解。我們?cè)賮砜纯聪旅娴拇a:

  System.out.println(cls1 == cls2);
  System.out.println(cls2 == cls3);

我們運(yùn)行程序,會(huì)發(fā)現(xiàn)輸出了:

true
true

這三個(gè)玩意竟然是同一個(gè)東西,那么就很好解釋了:在java的虛擬機(jī)中,每一個(gè)類都會(huì)被保存成為一個(gè)字節(jié)碼,用來保存該類的信息如名字、父類、變量、方法等。一個(gè)類的字節(jié)碼在虛擬機(jī)中有且只有一個(gè),也就是在第一次加載該類的時(shí)候會(huì)將類的字節(jié)碼加載到j(luò)ava虛擬機(jī)中,而上面有三種方法可以從虛擬機(jī)中獲取類的字節(jié)碼(PS:第三種方法最為常用),但是你別疑惑獲取這個(gè)字節(jié)碼干嘛嘛用,我們要反射嘛,說白了我們就是要來強(qiáng)暴這字節(jié)碼(Class)。。。。。(~ o ~)~zZ

 理解反射

  既然前面講解了Class類,現(xiàn)在我們可以開始講反射了。反射是什么呢?反射就是將類的各種成分映射成各種類,我們知道一個(gè)java類可以用一個(gè)class的對(duì)象來表示,這個(gè)類的組成成分有名字、變量、構(gòu)造方法等信息,我們當(dāng)然可以用一個(gè)個(gè)java類的表示。換句話說,表示java類的Class類提供了一系列的方法給我們用來獲取其中的變量、方法、構(gòu)造方法等信息,這些信息也有相應(yīng)的類的實(shí)例來表示,也就是Field、Method、Constructor等等;蛘吒ㄋ椎恼f,F(xiàn)ield就是java類中的所有變量的抽象、同理Method就是java類中所有方法的抽象,如果還是看不懂,很正常,往下看代碼估計(jì)更好理解。

構(gòu)造方法的反射

  從前面我們知道,Constructor就是java類所有構(gòu)造方法的抽象。那么我們?cè)趺赐ㄟ^反射來獲取類的構(gòu)造方法呢,參見代碼:


public class Test{ public static void main(String[] args) throws Exception {
        
        Class cls = Person.class;//獲取Person類的字節(jié)碼  Constructor constructor1 = cls.getConstructor();//調(diào)用getConstructor()獲取Person無(wú)參構(gòu)造方法 Person p1 = (Person) constructor1.newInstance();//通過調(diào)用newInstance()來執(zhí)行無(wú)參構(gòu)造方法  Constructor constructor2 = cls.getConstructor(int.class);//調(diào)用getConstructor(*.class)獲取Person帶參構(gòu)造方法 Person p2 = (Person) constructor2.newInstance(1);//通過調(diào)用newInstance(int)來執(zhí)行帶參構(gòu)造方法  }

} class Person{ public Person(){System.out.println("無(wú)參構(gòu)造方法");} public Person(int i){System.out.println("帶參構(gòu)造方法");}
}

控制臺(tái)輸出:

無(wú)參構(gòu)造方法
帶參構(gòu)造方法

  這里我們開始講解一下,代碼通過Person.class來獲取Person類的字節(jié)碼并將其保存在一個(gè)Class類的實(shí)例cls中,然后再通過cls.getConstructor()來獲取字節(jié)碼中的構(gòu)造方法并將其放入Constructor的實(shí)例constructor之中,很明顯,這個(gè)constructor并不是Person的構(gòu)造方法,而是保存Person構(gòu)造方法的一個(gè)實(shí)例,所以我們可以通過調(diào)用newInstance()來獲取保存在constructor中的person類的構(gòu)造方法并執(zhí)行,構(gòu)造方法執(zhí)行并返回一個(gè)Object的實(shí)例,并將其強(qiáng)轉(zhuǎn)為Person并保存在person的變量中,這就是調(diào)用反射來獲取構(gòu)造方法生成實(shí)例的全過程。

  在代碼中,我們也可以知道怎么獲取帶參的構(gòu)造方法,這是我們需要在getConstructor()是傳入構(gòu)造方法對(duì)應(yīng)參數(shù)的字節(jié)碼,例如代碼中Person(int i)我們需要傳入一個(gè)int.class(或者是Integet.TYPE)的字節(jié)碼提供給Class定位需要獲取的構(gòu)造方法。但是如果你比較貪心想獲取全部的構(gòu)造方法,沒問題,通過getConstructors():

Class cls = Person.class;//獲取Person類的字節(jié)碼 Constructor[] constructors = cls.getConstructors();//調(diào)用getConstructor()獲取Person無(wú)參構(gòu)造方法 for(Constructor c : constructors){
  //Person p = c.newInstance(****);遍歷執(zhí)行構(gòu)造方法 }

然后通過for循環(huán),就可以處理你所需要的構(gòu)造方法了。

成員變量的反射

  我們說完了構(gòu)造方法的反射,我們就接下來談?wù)劤蓡T變量的反射的用法。慣例還是先看代碼:


public class Test{ public static void main(String[] args) throws Exception {
        
        Person p = new Person("小紅", 20);
        
        Class cls = Class.forName("com.net168.test.Person");
        Field fieldName = cls.getField("name"); //fieldNmae的值是小紅嗎?錯(cuò)!它只是代表Person類身上name的這個(gè)變量,并沒有對(duì)應(yīng)到對(duì)象身上 // System.out.println(fieldNmae); //fieldNmae不代表具體的值,只代表一個(gè)變量,所以我們需要傳入一個(gè)person實(shí)例才能獲取到其對(duì)應(yīng)的值  System.out.println(fieldName.get(p));
    }

} class Person{ public Person(String name, int age){ this.name = name; this.age = age;
    } public String name; private int age;//對(duì)于某些人來說,年齡是秘密! }

  跟構(gòu)造方法的反射的實(shí)現(xiàn)差不多,我們也是先通過獲取Person的字節(jié)碼cls,然后從其中將Person的成員變量映射成一個(gè)Field類,在這里我們將Person.name這個(gè)變量映射成fieldName這個(gè)對(duì)象,當(dāng)然我們不可能單純的從fieldName這個(gè)對(duì)象中獲取咱們的“小紅”,因?yàn)閒ieldName是從cls中獲取的而并不是從person的實(shí)例中獲取的,所以它值并不是小紅;而是我們可以通過小紅這個(gè)person的實(shí)例p與fieldName聯(lián)系起來,也就是調(diào)用fieldName.get(p)才能獲取小紅這個(gè)字符串。

  但是我們?nèi)绻氆@取小紅年齡呢,女人的年齡大多是秘密,私有變量我們也可以這樣獲取嗎?修改下代碼:

Person p = new Person("小紅", 20);
Class cls = Class.forName("com.net168.test.Person");
Field fieldAge = cls.getField("age");
System.out.println(fieldAge.get(p));

執(zhí)行結(jié)果是:

Exception in thread "main" java.lang.NoSuchFieldException: age
at java.lang.Class.getField(Unknown Source)
at com.net168.test.Test.main(Test.java:11)

沒有這個(gè)字段,明明是有這個(gè)age的字段呀!但是我們發(fā)現(xiàn),原來這個(gè)女生的年齡是私有的,她就是不肯告訴咱們啊,那怎么辦?她不想告訴我們,我們就沒法知道了嗎?屌絲是不會(huì)那么容易屈服的!所以我們可以稍作一點(diǎn)處理,如下:

Person p = new Person("小紅", 20);
Class cls = Class.forName("com.net168.test.Person");
Field fieldAge = cls.getDeclaredField("age");//獲取類的私有變量 fieldAge.setAccessible(true);//設(shè)置該私有變量可被外面訪問 System.out.println(fieldAge.get(p));

可以通過getDeclaredField()來獲取Person類的私有變量,而且我們還可以在獲取到外界看不到的私有變量后,再通過setAccessible(true)設(shè)置該私有變量可以被強(qiáng)制訪問。暴力吧,JAVA的反射也被有些人叫做暴力反射。。。運(yùn)行代碼,我們就知道了小紅的芳齡 了:20

成員方法的反射

  如果大家看懂了前面成員變量和構(gòu)造方法的反射,基本上再了解成員方法的反射就沒有什么困難了,不賣關(guān)子,還是先上下代碼:


public class Test{ public static void main(String[] args) throws Exception {
        Person p = new Person();
        Class cls = p.getClass();//獲取Person的字節(jié)碼 //獲取setName()方法,需要傳入?yún)?shù)為String Method method1 = cls.getMethod("setName", String.class);
        method1.invoke(p, "小明");//關(guān)聯(lián)p,輸入“小明”并執(zhí)行該方法 //獲取getName()方法,無(wú)參則設(shè)為null Method method2 = cls.getMethod("getName", null);
        String name = (String) method2.invoke(p, null);//invoke返回的類型為Object  System.out.println(name); //獲取靜態(tài)方法,由于靜態(tài)方法只依賴與類,所以不需要提供具體的實(shí)例 Method method3 = cls.getMethod("show", int.class); // method3.invoke(p, 1);提供具體實(shí)例p也可通過編譯 method3.invoke(null, 1);
        
    }
} class Person{ public String name; public String getName(){ return name;
    } public void setName(String name){ this.name = name;
        System.out.println("設(shè)置name值為:" + name);
    } public static void show(int i){
        System.out.println("這是一個(gè)靜態(tài)方法:" + i);
    }
}

程序運(yùn)行結(jié)果:

設(shè)置name值為:小明
小明
這是一個(gè)靜態(tài)方法:1

  在方法的反射中,我們是利用了Method這個(gè)類,由于跟構(gòu)造方法類似,所以我不就再就獲取有參無(wú)參的方法的不同之處進(jìn)行講解?傮w來說,就是通過Person的字節(jié)碼獲取到Person類中對(duì)應(yīng)的方法并將其保存到Method的一個(gè)對(duì)象中,然后通過這個(gè)對(duì)象跟Person的具體實(shí)例進(jìn)行搭配,通過invoke()就可以調(diào)用到具體實(shí)例的對(duì)應(yīng)方法。在這里我們需要注意的時(shí)靜態(tài)方法的反射,由于靜態(tài)方法屬于一個(gè)類并不是屬于特定的一個(gè)對(duì)象,所以我們?cè)谡{(diào)用靜態(tài)方法的invoke()時(shí),并不需要傳入一個(gè)對(duì)象,當(dāng)然你非要傳入一個(gè)具體的實(shí)例也是沒有關(guān)系的,答案依然正確。

 


標(biāo)簽: 代碼

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:學(xué)習(xí)使用NSURLSession

下一篇:flappy bird游戲源代碼揭秘和下載 —— 可運(yùn)行于android、ios和html5多平臺(tái)