反射是框架設計的靈魂罗晕,使用的前提條件:必須先得到代表的字節(jié)碼的Class琅坡,Class類用于表示.class文件责鳍,即字節(jié)碼
一、反射的概述
JAVA反射機制是在運行狀態(tài)中泣港,對于任意一個類暂殖,都能夠知道這個類的所有屬性和方法;對于任意一個對象当纱,都能夠調(diào)用它的任意一個方法和屬性呛每;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制。
要想解剖一個類,必須先要獲取到該類的字節(jié)碼文件對象坡氯。而解剖使用的就是Class類中的方法.所以先要獲取到每一個字節(jié)碼文件對應的Class類型的對象.
以上的總結(jié)就是什么是反射
反射就是把java類中的各種成分映射成一個個的Java對象
例如:一個類有:成員變量晨横、方法、構造方法箫柳、包等等信息手形。利用反射技術可以對一個類進行解剖,把個個組成部分映射成一個個對象悯恍。
如圖是類的正常加載過程:反射的原理在與class對象库糠。
熟悉一下加載的時候:Class對象的由來是將class文件讀入內(nèi)存,并為之創(chuàng)建一個Class對象涮毫。
其中這個Class對象很特殊瞬欧。我們先了解一下這個Class類
二、查看Class類在java中的api詳解
Class 類的實例表示正在運行的 Java 應用程序中的類和接口罢防。也就是jvm中有N多的實例每個類都有該Class對象黍判。(包括基本數(shù)據(jù)類型)
Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及通過調(diào)用類加載器中的defineClass 方法自動構造的篙梢。也就是這不需要我們自己去處理創(chuàng)建顷帖,JVM已經(jīng)幫我們創(chuàng)建好了。
沒有公共的構造方法,方法共有64個太多了贬墩。下面用到哪個就詳解哪個吧
三榴嗅、反射的使用(這里使用Student類做演示)
先寫一個Student類。
1陶舞、獲取Class對象的三種方式
1.1 Object ——> getClass();
1.2 任何數(shù)據(jù)類型(包括基本數(shù)據(jù)類型)都有一個“靜態(tài)”的class屬性
1.3 通過Class類的靜態(tài)方法:forName(String? className)(常用)
其中1.1是因為Object類中的getClass方法嗽测、因為所有類都繼承Object類。從而調(diào)用Object類來獲取
packagefanshe;
/**
*?獲取Class對象的三種方式
*?1?Object?——>?getClass();
*?2?任何數(shù)據(jù)類型(包括基本數(shù)據(jù)類型)都有一個“靜態(tài)”的class屬性
*?3?通過Class類的靜態(tài)方法:forName(String??className)(常用)
*
*/
publicclassFanshe{
publicstaticvoidmain(String[]?args){
//第一種方式獲取Class對象
? ? ? ?//這一new?產(chǎn)生一個Student對象肿孵,一個Class對象唠粥。
Student?stu1?=newStudent();
Class?stuClass?=?stu1.getClass();//獲取Class對象
System.out.println(stuClass.getName());
//第二種方式獲取Class對象
Class?stuClass2?=?Student.class;
//判斷第一種方式獲取的Class對象和第二種方式獲取的是否是同一個
System.out.println(stuClass?==?stuClass2);
//第三種方式獲取Class對象
try{
//注意此字符串必須是真實路徑,就是帶包名的類路徑停做,包名.類名
Class?stuClass3?=?Class.forName("fanshe.Student");
//判斷三種方式是否獲取的是同一個Class對象
System.out.println(stuClass3?==?stuClass2);
}catch(ClassNotFoundException?e)?{
e.printStackTrace();
}
}
}
注意:在運行期間晤愧,一個類,只有一個Class對象產(chǎn)生蛉腌。
三種方式常用第三種官份,第一種對象都有了還要反射干什么。第二種需要導入類的包烙丛,依賴太強舅巷,不導包就拋編譯錯誤。一般都第三種河咽,一個字符串可以傳入也可寫在配置文件中等多種方法钠右。
2、通過反射獲取構造方法并使用:
student類:
packagefanshe;
publicclassStudent{
//---------------構造方法-------------------
//(默認的構造方法)
Student(String?str){
System.out.println("(默認)的構造方法?s?=?"+?str);
}
//無參構造方法
publicStudent(){
System.out.println("調(diào)用了公有忘蟹、無參構造方法執(zhí)行了飒房。。寒瓦。");
}
//有一個參數(shù)的構造方法
publicStudent(charname){
System.out.println("姓名:"+?name);
}
//有多個參數(shù)的構造方法
publicStudent(String?name?,intage){
//這的執(zhí)行效率有問題,以后解決坪仇。
System.out.println("姓名:"+name+"年齡:"+?age);
}
//受保護的構造方法
protectedStudent(booleann){
System.out.println("受保護的構造方法?n?=?"+?n);
}
//私有構造方法
privateStudent(intage){
System.out.println("私有的構造方法???年齡:"+?age);
}
}
共有6個構造方法利术;
測試類:
packagefanshe;
importjava.lang.reflect.Constructor;
/*
*?通過Class對象可以獲取某個類中的:構造方法恋昼、成員變量、成員方法;并訪問成員尺碰;
*
*?1.獲取構造方法:
*?????????1).批量的方法:
*?????????????public?Constructor[]?getConstructors():所有"公有的"構造方法
public?Constructor[]?getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護壶运、默認耕突、公有)
*?????????2).獲取單個的方法,并調(diào)用:
*?????????????public?Constructor?getConstructor(Class...?parameterTypes):獲取單個的"公有的"構造方法:
*?????????????public?Constructor?getDeclaredConstructor(Class...?parameterTypes):獲取"某個構造方法"可以是私有的羡蛾,或受保護漓帅、默認、公有;
*
*?????????????調(diào)用構造方法:
*?????????????Constructor-->newInstance(Object...?initargs)
*/
publicclassConstructors{
publicstaticvoidmain(String[]?args)throwsException{
//1.加載Class對象
Class?clazz?=?Class.forName("fanshe.Student");
//2.獲取所有公有構造方法
System.out.println("*************所有公有構造方法*******");
Constructor[]?conArray?=?clazz.getConstructors();
for(Constructor?c?:?conArray){
System.out.println(c);
}
System.out.println("*******所有的構造方法(包括:私有忙干、受保護器予、默認、公有)*****");
conArray?=?clazz.getDeclaredConstructors();
for(Constructor?c?:?conArray){
System.out.println(c);
}
System.out.println("*****************獲取公有捐迫、無參的構造方法*******************************");
Constructor?con?=?clazz.getConstructor(null);
//1>乾翔、因為是無參的構造方法所以類型是一個null,不寫也可以:這里需要的是一個參數(shù)的類型,切記是類型
//2>施戴、返回的是描述這個無參構造函數(shù)的類對象反浓。
System.out.println("con?=?"+?con);
//調(diào)用構造方法
Object?obj?=?con.newInstance();
//??System.out.println("obj?=?"?+?obj);
//??Student?stu?=?(Student)obj;
System.out.println("******獲取私有構造方法,并調(diào)用******");
con?=?clazz.getDeclaredConstructor(char.class);
System.out.println(con);
//調(diào)用構造方法
? ? ? ?//暴力訪問(忽略掉訪問修飾符)
con.setAccessible(true);
obj?=?con.newInstance('男');
}
}
后臺輸出:
**********************所有公有構造方法*********************************
publicfanshe.Student(java.lang.String,int)
publicfanshe.Student(char)
publicfanshe.Student()
************所有的構造方法(包括:私有赞哗、受保護雷则、默認、公有)***************
privatefanshe.Student(int)
protectedfanshe.Student(boolean)
publicfanshe.Student(java.lang.String,int)
publicfanshe.Student(char)
publicfanshe.Student()
fanshe.Student(java.lang.String)
*****************獲取公有懈玻、無參的構造方法*******************************
con?=publicfanshe.Student()
調(diào)用了公有巧婶、無參構造方法執(zhí)行了。涂乌。艺栈。
******************獲取私有構造方法,并調(diào)用*******************************
publicfanshe.Student(char)
姓名:男
調(diào)用方法:
1.獲取構造方法:
? 1).批量的方法:
publicConstructor[]?getConstructors():所有"公有的"構造方法
publicConstructor[]?getDeclaredConstructors():獲取所有的構造方法(包括私有湾盒、受保護湿右、默認、公有)
? 2).獲取單個的方法罚勾,并調(diào)用:
publicConstructorgetConstructor(Class...?parameterTypes):獲取單個的"公有的"構造方法:
publicConstructorgetDeclaredConstructor(Class...?parameterTypes):獲取"某個構造方法"可以是私有的毅人,或受保護、默認尖殃、公有丈莺;
?調(diào)用構造方法:
Constructor-->newInstance(Object... initargs)
2、newInstance是 Constructor類的方法(管理構造函數(shù)的類)
api的解釋為:
newInstance(Object... initargs):
使用此 Constructor 對象表示的構造方法來創(chuàng)建該構造方法的聲明類的新實例送丰,并用指定的初始化參數(shù)初始化該實例缔俄。它的返回值是T類型,所以newInstance是創(chuàng)建了一個構造方法的聲明類的新實例對象器躏。并為之調(diào)用
3俐载、獲取成員變量并調(diào)用
student類:
packagefanshe.field;
publicclassStudent{
publicStudent(){
}
//**********字段*************//
publicString?name;
protectedintage;
charsex;
privateString?phoneNum;
@Override
publicStringtoString(){
return"Student?[name="+?name?+",?age="+?age?+",?sex="+?sex
+",?phoneNum="+?phoneNum?+"]";
}
}
測試類:
packagefanshe.field;
importjava.lang.reflect.Field;
/*
*?獲取成員變量并調(diào)用:
*
*?1.批量的
*?????????1).Field[]?getFields():獲取所有的"公有字段"
*?????????2).Field[]?getDeclaredFields():獲取所有字段,包括:私有登失、受保護遏佣、默認、公有揽浙;
*?2.獲取單個的:
*?????????1).public?Field?getField(String?fieldName):獲取某個"公有的"字段状婶;
*?????????2).public?Field?getDeclaredField(String?fieldName):獲取某個字段(可以是私有的)
*
*??????設置字段的值:
*?????????Field?-->?public?void?set(Object?obj,Object?value):
*?????????????????????參數(shù)說明:
*?????????????????????1.obj:要設置的字段所在的對象意敛;
*?????????????????????2.value:要為字段設置的值;
*
*/
publicclassFields{
publicstaticvoidmain(String[]?args)throwsException{
//1.獲取Class對象
Class?stuClass?=?Class.forName("fanshe.field.Student");
//2.獲取字段
System.out.println("************獲取所有公有的字段********************");
Field[]?fieldArray?=?stuClass.getFields();
for(Field?f?:?fieldArray){
System.out.println(f);
}
System.out.println("************獲取所有的字段(包括私有太抓、受保護空闲、默認的)********************");
fieldArray?=?stuClass.getDeclaredFields();
for(Field?f?:?fieldArray){
System.out.println(f);
}
System.out.println("*************獲取公有字段**并調(diào)用***********************************");
Field?f?=?stuClass.getField("name");
System.out.println(f);
//獲取一個對象
Object?obj?=?stuClass.getConstructor().newInstance();//產(chǎn)生Student對象--》Student?stu?=?new?Student();
//為字段設置值
f.set(obj,"劉德華");//為Student對象中的name屬性賦值--》stu.name?=?"劉德華"
//驗證
Student?stu?=?(Student)obj;
System.out.println("驗證姓名:"+?stu.name);
System.out.println("**************獲取私有字段****并調(diào)用********************************");
f?=?stuClass.getDeclaredField("phoneNum");
System.out.println(f);
f.setAccessible(true);//暴力反射,解除私有限定
f.set(obj,"18888889999");
System.out.println("驗證電話:"+?stu);
}
}
后臺輸出:
************獲取所有公有的字段********************
publicjava.lang.String?fanshe.field.Student.name
************獲取所有的字段(包括私有走敌、受保護碴倾、默認的)********************
publicjava.lang.String?fanshe.field.Student.name
protectedintfanshe.field.Student.age
charfanshe.field.Student.sex
privatejava.lang.String?fanshe.field.Student.phoneNum
*************獲取公有字段**并調(diào)用***********************************
publicjava.lang.String?fanshe.field.Student.name
驗證姓名:劉德華
**************獲取私有字段****并調(diào)用********************************
privatejava.lang.String?fanshe.field.Student.phoneNum
驗證電話:Student?[name=劉德華,?age=0,?sex=
由此可見
調(diào)用字段時:需要傳遞兩個參數(shù):
Object obj = stuClass.getConstructor().newInstance();//產(chǎn)生Student對象--》Student stu = new Student();
//為字段設置值
f.set(obj, "劉德華");//為Student對象中的name屬性賦值--》stu.name = "劉德華"
第一個參數(shù):要傳入設置的對象,第二個參數(shù):要傳入實參
4掉丽、獲取成員方法并調(diào)用
student類:
packagefanshe.method;
publicclassStudent{
//**************成員方法***************//
publicvoidshow1(String?s){
System.out.println("調(diào)用了:公有的跌榔,String參數(shù)的show1():?s?=?"+?s);
}
protectedvoidshow2(){
System.out.println("調(diào)用了:受保護的,無參的show2()");
}
voidshow3(){
System.out.println("調(diào)用了:默認的捶障,無參的show3()");
}
privateStringshow4(intage){
System.out.println("調(diào)用了僧须,私有的,并且有返回值的项炼,int參數(shù)的show4():?age?=?"+?age);
return"abcd";
}
}
測試類:
packagefanshe.method;
importjava.lang.reflect.Method;
/*
*?獲取成員方法并調(diào)用:
*
*?1.批量的:
*?????????public?Method[]?getMethods():獲取所有"公有方法"担平;(包含了父類的方法也包含Object類)
*?????????public?Method[]?getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的)
*?2.獲取單個的:
*?????????public?Method?getMethod(String?name,Class...?parameterTypes):
*?????????????????????參數(shù):
*?????????????????????????name?:?方法名锭部;
*?????????????????????????Class?...?:?形參的Class類型對象
*?????????public?Method?getDeclaredMethod(String?name,Class...?parameterTypes)
*
*??????調(diào)用方法:
*?????????Method?-->?public?Object?invoke(Object?obj,Object...?args):
*?????????????????????參數(shù)說明:
*?????????????????????obj?:?要調(diào)用方法的對象暂论;
*?????????????????????args:調(diào)用方式時所傳遞的實參;
):
*/
publicclassMethodClass{
publicstaticvoidmain(String[]?args)throwsException{
//1.獲取Class對象
Class?stuClass?=?Class.forName("fanshe.method.Student");
//2.獲取所有公有方法
System.out.println("***************獲取所有的”公有“方法*******************");
stuClass.getMethods();
Method[]?methodArray?=?stuClass.getMethods();
for(Method?m?:?methodArray){
System.out.println(m);
}
System.out.println("***************獲取所有的方法拌禾,包括私有的*******************");
methodArray?=?stuClass.getDeclaredMethods();
for(Method?m?:?methodArray){
System.out.println(m);
}
System.out.println("***************獲取公有的show1()方法*******************");
Method?m?=?stuClass.getMethod("show1",?String.class);
System.out.println(m);
//實例化一個Student對象
Object?obj?=?stuClass.getConstructor().newInstance();
m.invoke(obj,"劉德華");
System.out.println("***************獲取私有的show4()方法******************");
m?=?stuClass.getDeclaredMethod("show4",int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object?result?=?m.invoke(obj,20);//需要兩個參數(shù)取胎,一個是要調(diào)用的對象(獲取有反射),一個是實參
System.out.println("返回值:"+?result);
}
}
控制臺輸出:
***************獲取所有的”公有“方法*******************
publicvoidfanshe.method.Student.show1(java.lang.String)
publicfinalvoidjava.lang.Object.wait(long,int)throwsjava.lang.InterruptedException
publicfinalnativevoidjava.lang.Object.wait(long)throwsjava.lang.InterruptedException
publicfinalvoidjava.lang.Object.wait()throwsjava.lang.InterruptedException
publicbooleanjava.lang.Object.equals(java.lang.Object)
publicjava.lang.String?java.lang.Object.toString()
publicnativeintjava.lang.Object.hashCode()
publicfinalnativejava.lang.Class?java.lang.Object.getClass()
publicfinalnativevoidjava.lang.Object.notify()
publicfinalnativevoidjava.lang.Object.notifyAll()
***************獲取所有的方法湃窍,包括私有的*******************
publicvoidfanshe.method.Student.show1(java.lang.String)
privatejava.lang.String?fanshe.method.Student.show4(int)
protectedvoidfanshe.method.Student.show2()
voidfanshe.method.Student.show3()
***************獲取公有的show1()方法*******************
publicvoidfanshe.method.Student.show1(java.lang.String)
調(diào)用了:公有的闻蛀,String參數(shù)的show1():?s?=?劉德華
***************獲取私有的show4()方法******************
privatejava.lang.String?fanshe.method.Student.show4(int)
調(diào)用了,私有的您市,并且有返回值的觉痛,int參數(shù)的show4():?age?=20
返回值:abcd
由此可見:
m = stuClass.getDeclaredMethod("show4", int.class);//調(diào)用制定方法(所有包括私有的),需要傳入兩個參數(shù)茵休,第一個是調(diào)用的方法名稱薪棒,第二個是方法的形參類型,切記是類型泽篮。
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object?result?=?m.invoke(obj,20);//需要兩個參數(shù)盗尸,一個是要調(diào)用的對象(獲取有反射)柑船,一個是實參
System.out.println("返回值:"+?result);
控制臺輸出:
***************獲取所有的”公有“方法*******************
publicvoidfanshe.method.Student.show1(java.lang.String)
publicfinalvoidjava.lang.Object.wait(long,int)throwsjava.lang.InterruptedException
publicfinalnativevoidjava.lang.Object.wait(long)throwsjava.lang.InterruptedException
publicfinalvoidjava.lang.Object.wait()throwsjava.lang.InterruptedException
publicbooleanjava.lang.Object.equals(java.lang.Object)
publicjava.lang.String?java.lang.Object.toString()
publicnativeintjava.lang.Object.hashCode()
publicfinalnativejava.lang.Class?java.lang.Object.getClass()
publicfinalnativevoidjava.lang.Object.notify()
publicfinalnativevoidjava.lang.Object.notifyAll()
***************獲取所有的方法帽撑,包括私有的*******************
publicvoidfanshe.method.Student.show1(java.lang.String)
privatejava.lang.String?fanshe.method.Student.show4(int)
protectedvoidfanshe.method.Student.show2()
voidfanshe.method.Student.show3()
***************獲取公有的show1()方法*******************
publicvoidfanshe.method.Student.show1(java.lang.String)
調(diào)用了:公有的,String參數(shù)的show1():?s?=?劉德華
***************獲取私有的show4()方法******************
privatejava.lang.String?fanshe.method.Student.show4(int)
調(diào)用了鞍时,私有的亏拉,并且有返回值的扣蜻,int參數(shù)的show4():?age?=20
返回值:abcd
其實這里的成員方法:在模型中有屬性一詞,就是那些setter()方法和getter()方法及塘。還有字段組成莽使,這些內(nèi)容在內(nèi)省中詳解
5、反射main方法
student類:
packagefanshe.main;
publicclassStudent{
publicstaticvoidmain(String[]?args){
System.out.println("main方法執(zhí)行了笙僚。芳肌。。");
}
}
測試類:
packagefanshe.main;
importjava.lang.reflect.Method;
/**
*?獲取Student類的main方法肋层、不要與當前的main方法搞混了
*/
publicclassMain{
publicstaticvoidmain(String[]?args){
try{
//1亿笤、獲取Student對象的字節(jié)碼
Class?clazz?=?Class.forName("fanshe.main.Student");
//2、獲取main方法
Method?methodMain?=?clazz.getMethod("main",?String[].class);//第一個參數(shù):方法名稱栋猖,第二個參數(shù):方法形參的類型净薛,
//3、調(diào)用main方法
//?methodMain.invoke(null,?new?String[]{"a","b","c"});
//第一個參數(shù)蒲拉,對象類型肃拜,因為方法是static靜態(tài)的,所以為null可以雌团,第二個參數(shù)是String數(shù)組燃领,這里要注意在jdk1.4時是數(shù)組,jdk1.5之后是可變參數(shù)
//這里拆的時候?qū)??new?String[]{"a","b","c"}?拆成3個對象辱姨。柿菩。。所以需要將它強轉(zhuǎn)雨涛。
methodMain.invoke(null,?(Object)newString[]{"a","b","c"});//方式一
//?methodMain.invoke(null,?new?Object[]{new?String[]{"a","b","c"}});//方式二
}catch(Exception?e)?{
e.printStackTrace();
}
}
}
控制臺輸出:
main方法執(zhí)行了枢舶。。替久。
6凉泄、反射方法的其它使用之---通過反射運行配置文件內(nèi)容
student類:
publicclassStudent{
publicvoidshow(){
System.out.println("is?show()");
}
}
配置文件以txt文件為例子(pro.txt):
className?=?cn.fanshe.Student
methodName?=?show
測試類:
importjava.io.FileNotFoundException;
importjava.io.FileReader;
importjava.io.IOException;
importjava.lang.reflect.Method;
importjava.util.Properties;
/*
*?我們利用反射和配置文件,可以使:應用程序更新時蚯根,對源碼無需進行任何修改
*?我們只需要將新類發(fā)送給客戶端后众,并修改配置文件即可
*/
publicclassDemo{
publicstaticvoidmain(String[]?args)throwsException{
//通過反射獲取Class對象
Class?stuClass?=?Class.forName(getValue("className"));//"cn.fanshe.Student"
//2獲取show()方法
Method?m?=?stuClass.getMethod(getValue("methodName"));//show
//3.調(diào)用show()方法
m.invoke(stuClass.getConstructor().newInstance());
}
//此方法接收一個key,在配置文件中獲取相應的value
publicstaticStringgetValue(String?key)throwsIOException{
Properties?pro?=newProperties();//獲取配置文件的對象
FileReader?in?=newFileReader("pro.txt");//獲取輸入流
pro.load(in);//將流加載到配置文件對象中
in.close();
returnpro.getProperty(key);//返回根據(jù)key獲取的value值
}
}
控制臺輸出:
is show()
需求:
當我們升級這個系統(tǒng)時颅拦,不要Student類蒂誉,而需要新寫一個Student2的類時,這時只需要更改pro.txt的文件內(nèi)容就可以了距帅。代碼就一點不用改動
要替換的student2類:
publicclassStudent2{
publicvoidshow2(){
System.out.println("is?show2()");
}
}
配置文件更改為:
className?=?cn.fanshe.Student2
methodName?=?show2
控制臺輸出:
is show2();
7右锨、反射方法的其它使用之---通過反射越過泛型檢查
泛型用在編譯期,編譯過后泛型擦除(消失掉)碌秸。所以是可以通過反射越過泛型檢查的绍移。
測試類:
importjava.lang.reflect.Method;
importjava.util.ArrayList;
/*
*?通過反射越過泛型檢查
*
*?例如:有一個String泛型的集合悄窃,怎樣能向這個集合中添加一個Integer類型的值?
*/
publicclassDemo{
publicstaticvoidmain(String[]?args)throwsException{
ArrayList?strList?=newArrayList<>();
strList.add("aaa");
strList.add("bbb");
//??strList.add(100);
//獲取ArrayList的Class對象蹂窖,反向的調(diào)用add()方法轧抗,添加數(shù)據(jù)
Class?listClass?=?strList.getClass();//得到?strList?對象的字節(jié)碼?對象
//獲取add()方法
Method?m?=?listClass.getMethod("add",?Object.class);
//調(diào)用add()方法
m.invoke(strList,100);
//遍歷集合
for(Object?obj?:?strList){
System.out.println(obj);
}
}
}
控制臺輸出:
aaa
bbb
100
8、總結(jié)
反射是各類框架的基礎瞬测,沒有反射也就沒有各類框架横媚。因此,反射也是面試吃绿耍考知識點分唾,希望能夠引起大家重視∈ǘ罚基本的反射API還是要熟悉绽乔,本文介紹了反射包下常用的一些方法,大家可以自己動手碼一遍代碼碳褒。更多關于反射的問題歡迎小伙伴們評論區(qū)留言討論折砸。