七、反射技術(shù)
反射技術(shù):其實就是動態(tài)加載一個指定的類限书,并獲取該類中的所有的內(nèi)容罩扇。并將字節(jié)碼文件中的內(nèi)容都封裝成對象,這樣便于操作這些成員狱掂。簡單說:反射技術(shù)可以對一個類進(jìn)行解剖。
反射的好處:大大的增強了程序的擴展性亲轨。
反射的基本步驟:
1趋惨、獲得Class對象,就是獲取到指定的名稱的字節(jié)碼文件對象惦蚊。
2器虾、實例化對象,獲得類的屬性养筒、方法或構(gòu)造函數(shù)曾撤。
3、訪問屬性晕粪、調(diào)用方法挤悉、調(diào)用構(gòu)造函數(shù)創(chuàng)建對象。
獲取這個Class對象巫湘,有三種方式:
1:通過每個對象都具備的方法getClass來獲取装悲。弊端:必須要創(chuàng)建該類對象,才可以調(diào)用getClass方法尚氛。
2:每一個數(shù)據(jù)類型(基本數(shù)據(jù)類型和引用數(shù)據(jù)類型)都有一個靜態(tài)的屬性class诀诊。弊端:必須要先明確該類。
前兩種方式不利于程序的擴展阅嘶,因為都需要在程序使用具體的類來完成属瓣。
3:使用的Class類中的方法,靜態(tài)的forName方法讯柔。
指定什么類名抡蛙,就獲取什么類字節(jié)碼文件對象,這種方式的擴展性最強魂迄,只要將類名的字符串傳入即可粗截。
// 1. 根據(jù)給定的類名來獲得 用于類加載
String classname = "cn.itcast.reflect.Person";// 來自配置文件
Class clazz = Class.forName(classname);// 此對象代表Person.class
// 2. 如果拿到了對象,不知道是什么類型 用于獲得對象的類型
Object obj = new Person();
Class clazz1 = obj.getClass();// 獲得對象具體的類型
// 3. 如果是明確地獲得某個類的Class對象 主要用于傳參
Class clazz2 = Person.class;
反射的用法:
1)捣炬、需要獲得java類的各個組成部分熊昌,首先需要獲得類的Class對象绽榛,獲得Class對象的三種方式:
Class.forName(classname) 用于做類加載
obj.getClass() 用于獲得對象的類型
類名.class 用于獲得指定的類型,傳參用
2)婿屹、反射類的成員方法:
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
3)灭美、反射類的構(gòu)造函數(shù):
Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
con.newInstance(params...)
4)、反射類的屬性:
Field field = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);
獲取了字節(jié)碼文件對象后选泻,最終都需要創(chuàng)建指定類的對象:
創(chuàng)建對象的兩種方式(其實就是對象在進(jìn)行實例化時的初始化方式):
1冲粤,調(diào)用空參數(shù)的構(gòu)造函數(shù):使用了Class類中的newInstance()方法。
2页眯,調(diào)用帶參數(shù)的構(gòu)造函數(shù):先要獲取指定參數(shù)列表的構(gòu)造函數(shù)對象梯捕,然后通過該構(gòu)造函數(shù)的對象的newInstance(實際參數(shù)) 進(jìn)行對象的初始化。
綜上所述窝撵,第二種方式傀顾,必須要先明確具體的構(gòu)造函數(shù)的參數(shù)類型,不便于擴展碌奉。所以一般情況下短曾,被反射的類,內(nèi)部通常都會提供一個公有的空參數(shù)的構(gòu)造函數(shù)赐劣。
// 如何生成獲取到字節(jié)碼文件對象的實例對象嫉拐。
Class clazz = Class.forName("cn.itcast.bean.Person");//類加載
// 直接獲得指定的類型
clazz = Person.class;
// 根據(jù)對象獲得類型
Object obj = new Person("zhangsan", 19);
clazz = obj.getClass();
Object obj = clazz.newInstance();//該實例化對象的方法調(diào)用就是指定類中的空參數(shù)構(gòu)造函數(shù),給創(chuàng)建對象進(jìn)行初始化魁兼。當(dāng)指定類中沒有空參數(shù)構(gòu)造函數(shù)時婉徘,該如何創(chuàng)建該類對象呢?請看method_2();
public static void method_2() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//既然類中沒有空參數(shù)的構(gòu)造函數(shù),那么只有獲取指定參數(shù)的構(gòu)造函數(shù),用該函數(shù)來進(jìn)行實例化咐汞。
//獲取一個帶參數(shù)的構(gòu)造器盖呼。
Constructor constructor = clazz.getConstructor(String.class,int.class);
//想要對對象進(jìn)行初始化,使用構(gòu)造器的方法newInstance();
Object obj = constructor.newInstance("zhagnsan",30);
//獲取所有構(gòu)造器化撕。
Constructor[] constructors = clazz.getConstructors();//只包含公共的
constructors = clazz.getDeclaredConstructors();//包含私有的
for(Constructor con : constructors) {
System.out.println(con);
}
}
反射指定類中的方法:
//獲取類中所有的方法几晤。
public static void method_1() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
Method[] methods = clazz.getMethods();//獲取的是該類中的公有方法和父類中的公有方法。
methods = clazz.getDeclaredMethods();//獲取本類中的方法植阴,包含私有方法蟹瘾。
for(Method method : methods) {
System.out.println(method);
}
}
//獲取指定方法;
public static void method_2() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//獲取指定名稱的方法掠手。
Method method = clazz.getMethod("show", int.class,String.class);
//想要運行指定方法热芹,當(dāng)然是方法對象最清楚,為了讓方法運行惨撇,調(diào)用方法對象的invoke方法即可,但是方法運行必須要明確所屬的對象和具體的實際參數(shù)府寒。
Object obj = clazz.newInstance();
method.invoke(obj, 39,"hehehe");//執(zhí)行一個方法
}
//想要運行私有方法魁衙。
public static void method_3() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//想要獲取私有方法报腔。必須用getDeclearMethod();
Method method = clazz.getDeclaredMethod("method", null);
// 私有方法不能直接訪問,因為權(quán)限不夠剖淀。非要訪問纯蛾,可以通過暴力的方式。
method.setAccessible(true);//一般很少用纵隔,因為私有就是隱藏起來翻诉,所以盡量不要訪問。
}
//反射靜態(tài)方法捌刮。
public static void method_4() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
Method method = clazz.getMethod("function",null);
method.invoke(null,null);
}