談談Java反射機制

寫在前面:
什么是java反射機制亮蒋?我們又為什么要學它?
當程序運行時负甸,允許改變程序結(jié)構(gòu)或變量類型流强,這種語言稱為動態(tài)語言。我們認為java并不是動態(tài)語言呻待,但是它卻有一個非常突出的動態(tài)相關機制打月,俗稱:反射。
IT行業(yè)里這么說蚕捉,沒有反射也就沒有框架僵控,現(xiàn)有的框架都是以反射為基礎。在實際項目開發(fā)中鱼冀,用的最多的是框架,填的最多的是類悠就,反射這一概念就是將框架和類揉在一起的調(diào)和劑千绪。所以,反射才是接觸項目開發(fā)的敲門磚梗脾!

配圖.jpg

一荸型、Class類
什么是Class類?
在面向?qū)ο蟮氖澜缋镎耄f事萬物皆是對象瑞妇。而在java語言中稿静,static修飾的東西不是對象,但是它屬于類辕狰。普通的數(shù)據(jù)類型不是對象改备,例如:int a = 5;它不是面向?qū)ο螅撬衅浒b類 Integer 或者分裝類來彌補了它蔓倍。除了以上兩種不是面向?qū)ο笮溆嗟陌愐灿兴拿嫦驅(qū)ο螅愂莏ava.lang.Class的實例化對象(注意Class是大寫)偶翅。也就是說:
Class A{}
當我創(chuàng)建了A類默勾,那么類A本身就是一個對象,誰的對象聚谁?java.lang.Class的實例對象母剥。
那么這個對象又該怎么表示呢?
我們先看一下下面這段代碼:

public class Demo(){
F f=new F();
}
class F{}

這里的F的實例化對象就可以用f表達出來形导。同理F類也是一個實例化對象环疼,Class類的實例化對象。我們可以理解為任何一個類都是Class類的實例化對象骤宣,這種實例化對象有三種表示方法:

public class Demo(){
F f=new F();
//第一種表達方式
Class c1=F.class;//這種表達方式同時也告訴了我們?nèi)魏我粋€類都有一個隱含的靜態(tài)成員變量class
//第二種表達方式
Class c2=f.getClass();//這種表達方式在已知了該類的對象的情況下通過getClass方法獲取
//第三種表達方式
Class c3 = null;
try {
c3 = Class.forName("com.text.F");//類的全稱
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
class F{}

以上三種表達方式秦爆,c1,c2,c3都表示了F類的類類型,也就是官方解釋的Class Type憔披。
那么問題來了:
System.out.println(c1 == c2)? or System.out.println(c1 == c3)?

答案是肯定的等限,返回值為ture。這表明不論c1 or c2 or c3都代表了F類的類類型芬膝,也就是說一個類只可能是Class類的一個實例對象望门。
理解了Class的概念,我們也可以通過類的類類型創(chuàng)建該類的對象實例锰霜,用c1 or c2 or c3的newInstance()方法:

Public class Demo1{
try {
Foo foo = (Foo)c1.newInstance();//foo就表示F類的實例化對象
foo.print();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}}
class F{
void print(){
}
}

這里需要注意的是筹误,c1是F類的類類型,創(chuàng)建出來的就是F類的對象癣缅。如果a是A類的類類型厨剪,那么創(chuàng)建出來的對象也應該與之對應,屬于A類的對象友存。

二祷膳、方法的反射
Class類有一個最簡單的方法,getName():

public class Demo2 {
public static void main(String[] args) {
Class c1 = int.class;//int 的類類型
Class c2 = String.class;//String類的類類型
Class c3 = void.class;
System.out.println(c1.getName());
System.out.println(c2.getName());
System.out.println(c2.getSimpleName());
System.out.println(c3.getName());
}
}

getName方法可以打印出該類類型的類名稱屡立,我們也可以用getSimpleName()方法可以打印出不包含包名的類的名稱直晨。從上面代碼可以看出,基本的數(shù)據(jù)類型以及void關鍵字都是存在類類型的。
案例:

public class ClassUtil {
public static void printClassMethodMessage(Object obj){
//要獲取類的信息》》首先我們要獲取類的類類型
Class c = obj.getClass();
//我們知道Object類是一切類的父類勇皇,所以我們傳遞的是哪個子類的對象罩句,c就是該子類的類類型。
//接下來我們要獲取類的名稱
System.out.println("類的名稱是:"+c.getName());
/*
*我們知道敛摘,萬事萬物都是對象门烂,方法也是對象,是誰的對象呢着撩?
* 在java里面诅福,方法是Method類的對象
*一個成員方法就是一個Method的對象,那么Method就封裝了對這個成員
*方法的操作
*/
//如果我們要獲得所有的方法拖叙,可以用getMethods()方法氓润,這個方法獲取的是所有的Public的函數(shù),包括父類繼承而來的薯鳍。如果我們要獲取所有該類自己聲明的方法咖气,就可以用getDeclaredMethods()方法,這個方法是不問訪問權(quán)限的挖滤。
Method[] ms = c.getMethods();//c.getDeclaredMethods()
//接下來我們拿到這些方法之后干什么崩溪?我們就可以獲取這些方法的信息,比如方法的名字斩松。
//首先我們要循環(huán)遍歷這些方法
for(int i = 0; i < ms.length;i++){
//然后可以得到方法的返回值類型的類類型
Class returnType = ms[i].getReturnType();
//得到方法的返回值類型的名字
System.out.print(returnType.getName()+" ");
//得到方法的名稱
System.out.print(ms[i].getName()+"(");
//獲取參數(shù)類型--->得到的是參數(shù)列表的類型的類類型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
}

總結(jié)思路:
通過方法的反射得到該類的名稱步驟:
1.獲取該類的類類型
2.通過類類型獲取類的方法(getMethods())
3.循環(huán)遍歷所獲取到的方法
4.通過這些方法的getReturnType()得到返回值類型的類類型伶唯,又通過該類類型得到返回值類型的名字
5.getName()得到方法的名稱,getParameterTypes()獲取這個方法里面的參數(shù)類型的類類型惧盹。

三乳幸、成員變量的反射
首先我們需要認識到成員變量也是對象,是java.lang.reflect.Field類的對象钧椰,那么也就是說Field類封裝了關于成員變量的操作粹断。既然它封裝了成員變量,我們又該如何獲取這些成員變量呢嫡霞?它有這么一個方法:

public class ClassUtil {
public static void printFieldMessage(Object obj){
Class c = obj.getClass();
//Field[] fs = c.getFields();
}

這里的getFields()方法獲取的所有的public的成員變量的信息瓶埋。和方法的反射那里public的成員變量,也有一個獲取所有自己聲明的成員變量的信息:
Field[] fs = c.getDeclaredFields();

我們得到它之后诊沪,可以進行遍歷(既然封裝了Field的信息养筒,那么我們就可以得到Field類型)

for (Field field : fs) {
//得到成員變量的類型的類類型
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成員變量的名稱
String fieldName = field.getName();
System.out.println(typeName+" "+fieldName);
}

四、構(gòu)造函數(shù)的反射
不論是方法的反射端姚、成員變量的反射闽颇、構(gòu)造函數(shù)的反射,我們只需要知道:要想獲取類的信息寄锐,首先得獲取類的類類型。

public static void printConMessage(Object obj){
Class c = obj.getClass();
/*
* 首先構(gòu)造函數(shù)也是對象,是java.lang.Constructor類的對象
* 也就是java.lang. Constructor中封裝了構(gòu)造函數(shù)的信息
* 和前面說到的一樣橄仆,它也有兩個方法:
* getConstructors()方法獲取所有的public的構(gòu)造函數(shù)
* getDeclaredConstructors()方法得到所有的自己聲明的構(gòu)造函數(shù)
*/
//Constructor[] cs = c.getConstructors();
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) {
//我們知道構(gòu)造方法是沒有返回值類型的剩膘,但是我們可以:
System.out.print(constructor.getName()+"(");
//獲取構(gòu)造函數(shù)的參數(shù)列表》》得到的是參數(shù)列表的類類型
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}

五、Class類的動態(tài)加載類
如何動態(tài)加載一個類呢盆顾?
首先我們需要區(qū)分什么是動態(tài)加載怠褐?什么是靜態(tài)加載?我們普遍認為編譯時刻加載的類是靜態(tài)加載類您宪,運行時刻加載的類是動態(tài)加載類奈懒。我們舉一個例子:

Class A{
Public static void main(String[] args){
if("B".equal(args[0])){
B b=new B();
b.start();
}
if("C".equal(args[0])){
C c=new C();
C.start();
}
}
}

上面這一段代碼,當我們在用eclipse或者myeclipse的時候我們并不關心是否能夠通過編譯宪巨,當我們直接在cmd使用javac訪問A.java類的時候磷杏,就會拋出問題:

A.java:7:錯誤:找不到符號
B b=new B();
符號:  類B
位置: 類A
A.java:7:錯誤:找不到符號
B b=new B();
符號:  類B
位置: 類A
A.java:12:錯誤:找不到符號
C c=new C();
符號:  類C
位置: 類A
A.java:12:錯誤:找不到符號
C c=new C();
符號:  類C
位置: 類A
4個錯誤

或許我們理所當然的認為這樣應該是錯,類B根本就不存在捏卓。但是如果我們多思考一下极祸,就會發(fā)現(xiàn)B一定用嗎?不一定怠晴。C一定用嗎遥金?也不一定。那么好蒜田,現(xiàn)在我們就讓B類存在

Class B{
Public static void start(){
System.out.print("B...satrt");
}
}

現(xiàn)在我們就先 javac B.class,讓B類先開始編譯稿械。然后在運行javac A.class。結(jié)果是:

A.java:12:錯誤:找不到符號
C c=new C();
符號:  類C
位置: 類A
A.java:12:錯誤:找不到符號
C c=new C();
符號:  類C
位置: 類A
2個錯誤

我們再想冲粤,這個程序有什么問題美莫。如果你說沒有什么問題?C類本來就不存在吧狻茂嗓!那么問題來了B類已經(jīng)存在了,假設我現(xiàn)在就想用B科阎,我們這個程序用得了嗎述吸?答案是肯定的,用不了锣笨。那用不了的原因是什么蝌矛?因為我們這個程序是做的類的靜態(tài)加載,也就是說new創(chuàng)建對象是靜態(tài)加載類错英,在編譯時刻就需要加載所有的入撒,可能使用到的類。所以不管你用不用這個類椭岩。
現(xiàn)在B類是存在的茅逮,但是我們這個程序仍然用不了璃赡,因為會一直報C類有問題,所以B類我也用不了献雅。那么在實際應用當中碉考,我們肯定需要如果B類存在,B類我就能用挺身,當用C類的時候侯谁,你再告訴我錯了。如果說將來你有100個類章钾,只要其中一個類出現(xiàn)問題墙贱,其它99個類你都用不了。所以這并不是我們想要的贱傀。
我們想要的就是我用那個類就加載那個類惨撇,也就是常說的運行時刻加載,動態(tài)加載類窍箍。如何實現(xiàn)動態(tài)加載類呢串纺?我們可以建這么一個類:

Class All{
Public static void start(){
try{
Class cl= Class.forName(args[0]);
//通過類類型,創(chuàng)建該類的對象
cl.newInstance();
}catch(Exception e){
e.printStackTrace();
}
}
}

前面我們在分析Class實例化對象的方式的時候椰棘,Class.forName("類的全稱")纺棺,它不僅僅表示了類的類類型,還表示了動態(tài)加載類邪狞。當我們javac All.java的時候祷蝌,它不會報任何錯誤,也就是說在編譯的時候是沒有錯誤的帆卓。只有當我們具體用某個類的時候巨朦,那個類不存在,它才會報錯剑令。
如果加載的類是B類糊啡,就需要:
B bt = (B) cl.newInstance();

萬一加載的是C類呢,可以改成
C ct = (C) cl.newInstance();

但是如果我想用很多的類或者加載很多的類吁津,該怎么辦棚蓄?我們可以統(tǒng)一一個標準,不論C類還是B類或者其他的類碍脏,比如定義一個標準
Stand s = (Stand) cl.newInstance();

只要B類和C類都是這個標準的就行了梭依。

Class All{
Public static void start(){
try{
Class cl= Class.forName(args[0]);
//通過類類型,創(chuàng)建該類的對象
Stand s = (Stand) cl.newInstance();
s.start();
}catch(Exception e){
e.printStackTrace();
}
}
}
interface Stand {
Public void start();
}

現(xiàn)在如果我想要用B類典尾,我們只需要:

Class B implements Stand{
Public void start(){
System.out.print("B...satrt");
}
}

加載B類,編譯運行役拴。

javac B.java
javac Stand.java
java Stand B

結(jié)果:
B...satrt

如果以后想用某一個類,不需要重新編譯钾埂,只需要實現(xiàn)這個標準的接口即可河闰。只需要動態(tài)的加載新的東西就行了科平。
這就是動態(tài)加載類

如果大家覺得我寫的對你有幫助姜性,請順手點個贊支持一下唄匠抗;如果大家覺得我有寫的不對的地方,歡迎大家多多發(fā)言污抬。謝謝!

轉(zhuǎn)載請注明作者及文章出處噢绳军!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末印机,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子门驾,更是在濱河造成了極大的恐慌射赛,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奶是,死亡現(xiàn)場離奇詭異楣责,居然都是意外死亡,警方通過查閱死者的電腦和手機聂沙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門秆麸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人及汉,你說我怎么就攤上這事沮趣。” “怎么了坷随?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵房铭,是天一觀的道長。 經(jīng)常有香客問我温眉,道長缸匪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任类溢,我火速辦了婚禮凌蔬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豌骏。我一直安慰自己龟梦,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布窃躲。 她就那樣靜靜地躺著计贰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蒂窒。 梳的紋絲不亂的頭發(fā)上躁倒,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天荞怒,我揣著相機與錄音,去河邊找鬼秧秉。 笑死褐桌,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的象迎。 我是一名探鬼主播荧嵌,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼砾淌!你這毒婦竟也來了啦撮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤汪厨,失蹤者是張志新(化名)和其女友劉穎赃春,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劫乱,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡织中,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了衷戈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狭吼。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖脱惰,靈堂內(nèi)的尸體忽然破棺而出搏嗡,到底是詐尸還是另有隱情,我是刑警寧澤拉一,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布采盒,位于F島的核電站,受9級特大地震影響蔚润,放射性物質(zhì)發(fā)生泄漏磅氨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一嫡纠、第九天 我趴在偏房一處隱蔽的房頂上張望烦租。 院中可真熱鬧,春花似錦除盏、人聲如沸叉橱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窃祝。三九已至,卻和暖如春踱侣,著一層夾襖步出監(jiān)牢的瞬間粪小,已是汗流浹背大磺。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留探膊,地道東北人杠愧。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像逞壁,于是被迫代替她去往敵國和親流济。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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

  • 1. Java基礎部分 基礎部分的順序:基本語法腌闯,類相關的語法袭灯,內(nèi)部類的語法,繼承相關的語法绑嘹,異常的語法,線程的語...
    子非魚_t_閱讀 31,581評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理橘茉,服務發(fā)現(xiàn)工腋,斷路器,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 從三月份找實習到現(xiàn)在畅卓,面了一些公司擅腰,掛了不少,但最終還是拿到小米翁潘、百度趁冈、阿里、京東拜马、新浪渗勘、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,184評論 11 349
  • 多情的月光0閱讀 110評論 0 1
  • 每一個旅人的心中都有一座涼亭為疲憊找一處歸宿讓流浪的心得片刻安寧 以秦磚漢瓦為魂構(gòu)造成唐詩宋詞的律韻孤獨于山嶺田野...
    凡人語閱讀 338評論 0 2