1.什么是反射
官方解釋:反射允許對(duì)封裝類的字段、方法和構(gòu)造方法的信息進(jìn)行編程訪問(wèn)。
虛擬機(jī)加載類文件后,會(huì)在方法區(qū)生成一個(gè)類對(duì)象,包含了類的結(jié)構(gòu)信息,如字段、方法、構(gòu)造方法等。反射是一種能夠在程序運(yùn)行時(shí)動(dòng)態(tài)訪問(wèn)、修改類對(duì)象中任意屬性的機(jī)制(包括private屬性)。
Java 反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性;這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱為 Java 語(yǔ)言的反射機(jī)制。簡(jiǎn)單來(lái)說(shuō),反射機(jī)制指的是程序在運(yùn)行時(shí)能夠獲取自身的信息。在 Java 中,只要給定類的名字,就可以通過(guò)反射機(jī)制來(lái)獲得類的所有信息。
如果說(shuō)大家研究過(guò)框架的底層原理或者咱們自己寫(xiě)過(guò)框架的話,一定對(duì)反射這個(gè)概念不陌生。
反射之所以被稱為框架的靈魂,主要是因?yàn)樗x予了我們?cè)谶\(yùn)行時(shí)分析類以及執(zhí)行類中方法的能力。
通過(guò)反射你可以獲取任意一個(gè)類的所有屬性和方法,你還可以調(diào)用這些方法和屬性。
獲取類對(duì)象的四種方式
1.通過(guò)對(duì)象獲取到類對(duì)象.getClass();
Class personClass = person.getClass();
2.通過(guò) Class.forName()傳入類的全路徑獲取;裝入類,并做類的靜態(tài)初始化,返回Class的對(duì)象
Class personClass = Class.forName("全類名");
3.通過(guò)類屬性 ClassName.Class獲取類對(duì)象;
Class personClass = Person.Class;
4.通過(guò)ClassLoader.getSystemClassLoader().loadClass("全類名");通過(guò)類加載器獲取 Class 對(duì)象不會(huì)進(jìn)行初始化,意味著不進(jìn)行包括初始化等一系列步驟,靜態(tài)代碼塊和靜態(tài)對(duì)象不會(huì)得到執(zhí)行
注意:全類名是包名+類名,一般是程序src目錄下的包名+類名。
類對(duì)象中描述各個(gè)部分的對(duì)象:
- Class:描述整個(gè)類的對(duì)象
- Constructor:描述構(gòu)造方法的對(duì)象
Class clazz = Class.forName("Person"); //獲取類的所有構(gòu)造方法 Constructor[] constructorAll = clazz.getDeclaredConstructors(); for(Constructor c: constructorAll){ System.out.println(c); } Constructor constructor1 = clazz.getDeclaredConstructor(); Constructor constructor2 = clazz.getDeclaredConstructor(String.class); System.out.println(constructor1); System.out.println(constructor2); //可以獲取構(gòu)造方法中的一些內(nèi)容 int modifiers = constructor1.getModifiers(); System.out.println(modifiers); int parameterCount = constructor1.getParameterCount(); System.out.println(parameterCount); Parameter[] parameters = constructor2.getParameters(); for(Parameter p: parameters){ System.out.println(p); } //使用構(gòu)造方法創(chuàng)建對(duì)象 Person person = (Person) constructor1.newInstance(); System.out.println(person);
- Field:描述成員變量的對(duì)象
Class clazz = Class.forName("Person"); //獲取私有/非私有屬性 Field[] fields = clazz.getDeclaredFields(); for(Field f: fields){ System.out.println(f); } Field field = clazz.getDeclaredField("name"); System.out.println(field); //獲取權(quán)限修飾符 int modifiers = field.getModifiers(); System.out.println(modifiers); //獲取數(shù)據(jù)類型 String value = field.getName(); System.out.println( value);
- Method:描述成員方法的對(duì)象。
2.反射的功能
1.獲取一個(gè)類里面所有的信息和其他業(yè)務(wù)邏輯。
2.結(jié)合配置文件,動(dòng)態(tài)的創(chuàng)建對(duì)象并調(diào)用方法。
1、Java反射機(jī)制的核心是在程序運(yùn)行時(shí)動(dòng)態(tài)加載類并獲取類的詳細(xì)信息,從而操作類或?qū)ο蟮膶傩院头椒ā1举|(zhì)是JVM得到class對(duì)象之后,再通過(guò)class對(duì)象進(jìn)行反編譯,從而獲取對(duì)象的各種信息。
2、Java屬于先編譯再運(yùn)行的語(yǔ)言,程序中對(duì)象的類型在編譯期就確定下來(lái)了,而當(dāng)程序在運(yùn)行時(shí)可能需要?jiǎng)討B(tài)加載某些類,這些類因?yàn)橹坝貌坏?,所以沒(méi)有被加載到JVM。通過(guò)反射,可以在運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建對(duì)象并調(diào)用其屬性,不需要提前在編譯期知道運(yùn)行的對(duì)象是誰(shuí)。
3.序列化、反序列化
Serialization(序列化)是一種將對(duì)象以一連串的字節(jié)描述的過(guò)程;反序列化deserialization是一種將這些字節(jié)重建成一個(gè)對(duì)象的過(guò)程。將程序中的對(duì)象,放入文件中保存就是序列化,將文件中的字節(jié)碼重新轉(zhuǎn)成對(duì)象就是反序列化。
ObjectOutputStream 類用來(lái)序列化一個(gè)對(duì)象,可將對(duì)象序列化到一個(gè)文件中。
注意: 當(dāng)序列化一個(gè)對(duì)象到文件時(shí), 按照 Java 的標(biāo)準(zhǔn)約定是給文件一個(gè) .ser 擴(kuò)展名。
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
? ? ? ? //創(chuàng)建文件輸出流對(duì)象
FileOutputStream fileOutputStream = new FileOutputStream("D:\\Serializable\\Person.ser");
? ? ? ? //創(chuàng)建序列化對(duì)象
Person person = new Person("name",22,"女","black","boy");
? ? ? ? //創(chuàng)建對(duì)象輸出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
? ? ? ? //將對(duì)象序列化
objectOutputStream.writeObject(person);
? ? ? ? //創(chuàng)建文件輸入流對(duì)象
FileInputStream fileInputStream = new FileInputStream("D:\\Serializable\\Person.ser");
? ? ? ? //創(chuàng)建對(duì)象輸入流
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
? ? ? ? //反序列化
Person person1 = (Person) objectInputStream.readObject();
System.out.println(person1);
}
}
三、序列化和反序列化的注意點(diǎn):
1.序列化時(shí),只對(duì)對(duì)象的屬性進(jìn)行保存。
2.當(dāng)一個(gè)父類實(shí)現(xiàn)序列化,子類自動(dòng)實(shí)現(xiàn)序列化,不需要顯式實(shí)現(xiàn)Serializable接口。
3.當(dāng)一個(gè)對(duì)象的實(shí)例變量引用其他對(duì)象,序列化該對(duì)象時(shí)也把引用對(duì)象進(jìn)行序列化。
4.聲明為static和transient類型的成員數(shù)據(jù)不能被序列化。因?yàn)閟tatic代表類的狀態(tài),transient代表對(duì)象的臨時(shí)數(shù)據(jù)。
5.Java有很多基礎(chǔ)類已經(jīng)實(shí)現(xiàn)了serializable接口,比如String,Vector等。但是也有一些沒(méi)有實(shí)現(xiàn)serializable接口的。
6.序列號(hào)id,用于標(biāo)識(shí)序列號(hào)對(duì)象的版本,在反序列化的過(guò)程中,會(huì)使用序列化id來(lái)檢查序列化對(duì)象的版本與當(dāng)前類的版本是否匹配。
功能
-
對(duì)象隨著程序的運(yùn)行而創(chuàng)建,程序結(jié)束對(duì)象就銷毀,序列化可以讓對(duì)象持久的保持在內(nèi)存中
-
可以在進(jìn)程之間進(jìn)行對(duì)象傳輸
4.動(dòng)態(tài)代理
無(wú)侵入的為對(duì)象的方法增加新功能。
動(dòng)態(tài)代理的實(shí)現(xiàn)步驟:
- 定義接口,并定義要在代理對(duì)象中實(shí)現(xiàn)的方法(包括返回類型、參數(shù)列表、方法名)
- 創(chuàng)建一個(gè)實(shí)現(xiàn)InvocationHandler接口的類,并重寫(xiě)invoke方法,invoke方法在代理對(duì)象調(diào)用方法時(shí)觸發(fā),并允許在方法調(diào)用前后增加額外的邏輯。
- 使用Proxy類創(chuàng)建代理對(duì)象java.lang.reflect.Proxy類的靜態(tài)方法newProxyInstace來(lái)創(chuàng)建代理對(duì)象,該方法有三個(gè)參數(shù)。參數(shù)1:加載代理類的類加載器;參數(shù)2:目標(biāo)對(duì)象實(shí)現(xiàn)的接口;參數(shù)3:InvocationHandler對(duì)象,表示實(shí)現(xiàn)invoke方法的對(duì)象。
- 調(diào)用代理對(duì)象的方法,當(dāng)代理對(duì)象的方法被調(diào)用時(shí),invoke方法會(huì)被觸發(fā)。
public class Person implements IPerson{
private String name;
public Person(){
}
public Person(String name){
this.name = name;
}
@Override
public String eat(String thing){
System.out.println(this.name+"正在吃"+thing);
return thing;
}
@Override
public void run(){
System.out.println(this.name+"正在run");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public interface IPerson {
//把需要代理的方法定義在接口當(dāng)中
public abstract String eat(String name);
public abstract void run();
}
public class ProxyUtil {
/**
*為給定的對(duì)象生成一個(gè)代理對(duì)象
* @param person
*/
public static IPerson createProxy(Person person) {
/**
* 參數(shù)1:指定類加載器,去加載生成的代理類
* 參數(shù)2:指定接口,這個(gè)接口用于指定生成的代理長(zhǎng)什么,也就是有哪些方法
* 參數(shù)3:用來(lái)指定生成的代理對(duì)象要干什么事情
*/
IPerson iPerson = (IPerson) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader()
, new Class[]{IPerson.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 參數(shù)1:代理的對(duì)象
* 參數(shù)2:要運(yùn)行的方法
* 參數(shù)3:調(diào)用方法時(shí)傳遞的實(shí)參
*/
if("eat".equals(method.getName())){
System.out.println("拿筷子");
System.out.println("拿碗");
}else if("run".equals(method.getName())){
System.out.println("帶水");
System.out.println("穿褲子");
}
return method.invoke(person,args);
}
});
return iPerson;
}
}
? 實(shí)現(xiàn)原理:通過(guò)代理類生成字節(jié)碼文件,使用類加載器將文件加載到內(nèi)存,最終創(chuàng)建代理對(duì)象。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-690513.html
注意:動(dòng)態(tài)的生成字節(jié)碼文件,是在運(yùn)行時(shí)發(fā)生的。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-690513.html
到了這里,關(guān)于Java反射、代理機(jī)制的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!