?
在Java運(yùn)行時(shí)環(huán)境中裳凸,對(duì)于任意一個(gè)類薄货,能否知道這個(gè)類的哪些屬性和方法碌奉?對(duì)于任意一個(gè)對(duì)象短曾,能否調(diào)用它的任意一個(gè)方法?答案是肯定的赐劣。這種動(dòng)態(tài)獲取類的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能來(lái)自于Java語(yǔ)言的反射(Reflection)機(jī)制嫉拐。
反射給java提供了,運(yùn)行時(shí)獲取一個(gè)類實(shí)例的可能魁兼,這一點(diǎn)非常靈活婉徘,你僅僅傳一個(gè)類的全限定名,就能通過(guò)反射璃赡,來(lái)獲取對(duì)應(yīng)的類實(shí)例判哥,我們一般會(huì)用Class類,來(lái)調(diào)用這個(gè)被反射的Objcet類下的:構(gòu)造方法碉考,屬性塌计,或方法等。
反射在一些開源框架里用的非常之多侯谁,Spring锌仅,Struts,Hibnerate墙贱,MyBatics都有它的影子热芹,反射雖然很靈活,能夠使得寫的代碼惨撇,變的大幅精簡(jiǎn)伊脓,所以在用的時(shí)候,一定要注意具體的應(yīng)用場(chǎng)景魁衙。
反射的優(yōu)缺點(diǎn)如下:
優(yōu)點(diǎn):
A:能夠運(yùn)行時(shí)動(dòng)態(tài)獲取類的實(shí)例报腔,大大提高系統(tǒng)的靈活性和擴(kuò)展性。
B:與Java動(dòng)態(tài)編譯相結(jié)合剖淀,可以實(shí)現(xiàn)無(wú)比強(qiáng)大的功能
缺點(diǎn):
A:使用反射的性能較低
B:使用反射相對(duì)來(lái)說(shuō)不安全
C:破壞了類的封裝性纯蛾,可以通過(guò)反射獲取這個(gè)類的私有方法和屬性
任何事物,都有兩面性纵隔,反射的優(yōu)點(diǎn)翻诉,也同是就是它的缺點(diǎn)炮姨,所以,沒(méi)有好與壞碰煌,只有最合適的場(chǎng)景舒岸,一陰一陽(yáng),才是天道平衡的條件拄查。
在反射API中我們重點(diǎn)關(guān)注一下幾個(gè)類:
Class? -- 代表類
Field? -- 代表屬性(成員變量)
Method? -- 代表方法
Constructor? -- 代表構(gòu)造方法
一吁津、Class
Java中不論一個(gè)類產(chǎn)生了多少個(gè)對(duì)象,這些對(duì)象的Class對(duì)象都始終是一個(gè)堕扶。Class對(duì)象中含有該類的任何信息(屬性碍脏,方法,類名稍算,父類典尾,包等),在Java中獲取Class對(duì)象的方法有三種:
// 第一種方法:類名.class
Class ? cla = Student.class;
Class ? as =int.class;?? // 基本數(shù)據(jù)類型唯一能點(diǎn)出的就是class
// 第二種方法:通過(guò)對(duì)象調(diào)用.getClass()
Student ? stu =newStudent();
Class ? c = stu.getClass();
// 第三種方法:通過(guò)類的全限定名獲取
try{
Class ? c1 = Class.forName("entity.Student");
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(cla.getName()); // 全限定名
System.out.println(Modifier.toString(cla.getModifiers()));
System.out.println(cla.getSimpleName());
System.out.println(cla.getPackage().getName());
可以通過(guò)Class對(duì)象產(chǎn)生該類的對(duì)象糊探,如下:
// 獲取Class對(duì)象
Class ? cla = Student.class;
/* 1. 直接創(chuàng)建對(duì)象(調(diào)用默認(rèn)無(wú)參構(gòu)造方法)钾埂,類里必須要有默認(rèn)構(gòu)造方法 */
Object ? obj1 = cla.newInstance();
System.out.println(obj1);
二、Constructor
如果想通過(guò)有參構(gòu)造方法來(lái)創(chuàng)建對(duì)象科平,那么這時(shí)候就得先獲取有參構(gòu)造方法褥紫,再通過(guò)有參構(gòu)造方法來(lái)創(chuàng)建對(duì)象:
// 獲取Class對(duì)象
Class cla = Student.class;
/* 1. 直接創(chuàng)建對(duì)象(調(diào)用默認(rèn)無(wú)參構(gòu)造方法),類里必須要有默認(rèn)構(gòu)造方法 */
Object obj1 = cla.newInstance();
System.out.println(obj1);
/* 2. 通過(guò)無(wú)參構(gòu)造方法創(chuàng)建對(duì)象瞪慧,和第一種方法效果一樣 */
Constructor no = cla.getConstructor(); // 先獲取無(wú)參構(gòu)造方法
Object obj2 = no.newInstance();
System.out.println(obj2);
/* 3. 通過(guò)有參構(gòu)造方法創(chuàng)建對(duì)象 */
Constructor has = cla.getDeclaredConstructor(String.class,int.class); // 形參
Object obj3 = has.newInstance("老李", 23); // 傳入的是實(shí)參
System.out.println(obj3);
三髓考、Field
Field代表是類中的屬性,我們可以獲取屬性弃酌,并修改其值(注:先得有對(duì)象才能修改值氨菇,另:修改沒(méi)權(quán)限的屬性時(shí),需要先打開該屬性的權(quán)限)妓湘。
// 獲取Class對(duì)象
Class cla =newStudent().getClass();
// 獲取構(gòu)造方法
Constructor con = cla.getConstructor(String.class,int.class);
// 創(chuàng)建對(duì)象
Object obj = con.newInstance("如來(lái)", 222);
// 獲取要操作的屬性
Field name = cla.getDeclaredField("name");
// 反射操作private屬性的時(shí)候查蓉,需要打開權(quán)限
name.setAccessible(true);
// 獲取obj的name屬性值
System.out.println(name.get(obj));
// 把obj的name屬性值改為:菩提
name.set(obj, "菩提");
System.out.println(name.get(obj));
// 把id設(shè)置為10086
Field id = cla.getDeclaredField("id");
id.set(obj, 10086);
System.out.println(id.get(obj))
四、Method
Method代表類中的方法榜贴,和Field操作類型:
// 獲取Class對(duì)象
Class cla = Student.class;
// 創(chuàng)建對(duì)象
Object obj = cla.getDeclaredConstructor(String.class,int.class).newInstance("達(dá)摩", 666);
// 獲取要操作的方法
Method showNo = cla.getDeclaredMethod("show");
Method showHas = cla.getDeclaredMethod("show", String.class);
Method calc = cla.getDeclaredMethod("calc",int.class,double.class);
calc.setAccessible(true);
// 調(diào)用方法
showNo.invoke(obj);
showHas.invoke(obj, "老衲");
Object value = calc.invoke(obj, 10086, Math.PI);
System.out.println(value);
我們可以用反射來(lái)改進(jìn)簡(jiǎn)單工廠模式:
packagedemo08;
importjava.io.FileInputStream;
importjava.util.Properties;
publicclassPetFactory {
publicstaticvoidmain(String[] args) {
System.out.println(getInstance("dog")); ?// demo08.Dog@67a9b034
}
// 工廠方法
publicstaticPet ? getInstance(String tag) {
Properties ? p =newProperties();
try{
p.load(newFileInputStream("conf/pet.properties"));
}catch(Exception e) {
System.out.println("加載配置文件錯(cuò)誤豌研!");
}
String ? className = p.getProperty(tag);
try{
// 利用反射創(chuàng)建對(duì)象
Class ? cla = Class.forName(className);
return(Pet)cla.newInstance();
}catch(ClassNotFoundException e) {
System.out.println("無(wú)法識(shí)別您的標(biāo)識(shí)!");
}catch(InstantiationException e) {
e.printStackTrace();
}catch(IllegalAccessException e) {
e.printStackTrace();
}
returnnull;
}
}
classPet {}
classDogextendsPet {}
classCatextendsPet {}
classPenguinextendsPet {}
conf/pet.properties文件內(nèi)容如下:
dog=demo08.Dog
cat=demo08.Cat
penguin=demo08.Penguin
這里的配置文件為.properties,稱作屬性文件唬党。通過(guò)反射讀取里邊的內(nèi)容鹃共。這樣代碼是固定的,但是配置文件的內(nèi)容我們可以改初嘹,這樣使我們的代碼靈活了很多及汉!
綜上JAVA反射的再次學(xué)習(xí)沮趣,靈活的運(yùn)用它屯烦,能夠使我們的代碼更加靈活,但是它也有它的缺點(diǎn),就是運(yùn)用它會(huì)使我們的軟件的性能降低驻龟,復(fù)雜度增加温眉,所以還要我們慎重的使用它。
更多內(nèi)容關(guān)注微信公眾號(hào)mjw-java或訪問(wèn)www.moliying.com