Java反射--2021面試題系列教程(附答案解析)--大白話解讀--JavaPub版本
Java反射--2021面試題系列教程(附答案解析)--大白話解讀--JavaPub版本
前言
序言
再高大上的框架,也需要扎實(shí)的基礎(chǔ)才能玩轉(zhuǎn),高頻面試問題更是基礎(chǔ)中的高頻實(shí)戰(zhàn)要點(diǎn)。
適合閱讀人群
Java 學(xué)習(xí)者和愛好者,有一定工作經(jīng)驗(yàn)的技術(shù)人,準(zhǔn)面試官等。
閱讀建議
本教程是系列教程,包含 Java 基礎(chǔ),JVM,容器,多線程,反射,異常,網(wǎng)絡(luò),對象拷貝,JavaWeb,設(shè)計模式,Spring-Spring MVC,Spring Boot / Spring Cloud,Mybatis / Hibernate,Kafka,RocketMQ,Zookeeper,MySQL,Redis,Elasticsearch,Lucene。訂閱不迷路,2021奧利給。
JavaPub知識清單
微信搜:JavaPub,閱讀全套系列面試題教程
前言
序言
適合閱讀人群
閱讀建議
題目
前言
1.什么是反射?
2.什么是 java 序列化?什么情況下需要序列化?
3.動態(tài)代理是什么?有哪些應(yīng)用?
4.怎么實(shí)現(xiàn)動態(tài)代理?
如何使用Java的反射?
題目
前言
1.什么是反射?
百度百科:
Java的反射(reflection)機(jī)制是指在程序的運(yùn)行狀態(tài)中,可以構(gòu)造任意一個類的對象,可以了解任意一個對象所屬的類,可以了解任意一個類的成員變量和方法,可以調(diào)用任意一個對象的屬性和方法。這種動態(tài)獲取程序信息以及動態(tài)調(diào)用對象的功能稱為Java語言的反射機(jī)制。反射被視為動態(tài)語言的關(guān)鍵。
在Java運(yùn)行時環(huán)境中,對于任意一個類,能否知道這個類有哪些屬性和方法?對于任意一個對象,能否調(diào)用它的任意一個方法
Java反射機(jī)制主要提供了以下功能:
在運(yùn)行時判斷任意一個對象所屬的類。
在運(yùn)行時構(gòu)造任意一個類的對象。
在運(yùn)行時判斷任意一個類所具有的成員變量和方法。
在運(yùn)行時調(diào)用任意一個對象的方法。
JavaPub參考巨人:https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html
到這里如果你對 Java 反射還有一些疑惑,后面再 JavaPub 公眾號,還回做更詳細(xì)的講解。
2.什么是 java 序列化?什么情況下需要序列化?
序列化和反序列化是Java中最基礎(chǔ)的知識點(diǎn),也是很容易被大家遺忘的,雖然天天使用它,但并不一定都能清楚的說明白。我相信很多小伙伴們掌握的也就幾句概念、關(guān)鍵字(Serializable)而已,如果深究問一下序列化和反序列化是如何實(shí)現(xiàn)、使用場景等,就可能不知所措了。在每次我作為面試官,考察Java基礎(chǔ)時,通常都會問到序列化、反序列化的知識點(diǎn),用以衡量其Java基礎(chǔ)如何。當(dāng)被問及Java序列化是什么?反序列化是什么?什么場景下會用到?如果不用它,會出現(xiàn)什么問題等,一般大家回答也就是幾句簡單的概念而已,有的工作好幾年的應(yīng)聘者甚至連概念都說不清楚,一臉悶逼。
什么是序列化和反序列化
序列化是指將Java對象轉(zhuǎn)換為字節(jié)序列的過程,而反序列化則是將字節(jié)序列轉(zhuǎn)換為Java對象的過程。
Java對象序列化是將實(shí)現(xiàn)了?Serializable?接口的對象轉(zhuǎn)換成一個字節(jié)序列,能夠通過網(wǎng)絡(luò)傳輸、文件存儲等方式傳輸?,傳輸過程中卻不必?fù)?dān)心數(shù)據(jù)在不同機(jī)器、不同環(huán)境下發(fā)生改變,也不必關(guān)心字節(jié)的順序或其他任何細(xì)節(jié),并能夠在以后將這個字節(jié)序列完全恢復(fù)為原來的對象(恢復(fù)這一過程稱之為反序列化)。
對象的序列化是非常有趣的,因?yàn)槔盟梢詫?shí)現(xiàn)輕量級持久性,“持久性”意味著一個對象的生存周期不單單取決于程序是否正在運(yùn)行,它可以生存于程序的調(diào)用之間。通過將一個序列化對象寫入磁盤,然后在重新調(diào)用程序時恢復(fù)該對象,從而達(dá)到實(shí)現(xiàn)對象的持久性的效果。
本質(zhì)上講,序列化就是把實(shí)體對象狀態(tài)按照一定的格式寫入到有序字節(jié)流,反序列化就是從有序字節(jié)流重建對象,恢復(fù)對象狀態(tài)。
簡單說就是為了保存在內(nèi)存中的各種對象的狀態(tài)(也就是實(shí)例變量,不是方法),并且可以把保存的對象狀態(tài)再讀出來。雖然你可以用你自己的各種各樣的方法來保存object states,但是Java給你提供一種應(yīng)該比你自己好的保存對象狀態(tài)的機(jī)制,那就是序列化。
什么情況下需要序列化
當(dāng)你想把的內(nèi)存中的對象狀態(tài)保存到一個文件中或者數(shù)據(jù)庫中時候;
當(dāng)你想用套接字在網(wǎng)絡(luò)上傳送對象的時候;
當(dāng)你想通過RMI傳輸對象的時候;
為什么需要使用序列化和反序列化
我們知道,不同進(jìn)程/程序間進(jìn)行遠(yuǎn)程通信時,可以相互發(fā)送各種類型的數(shù)據(jù),包括文本、圖片、音頻、視頻等,而這些數(shù)據(jù)都會以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳送。
那么當(dāng)兩個Java進(jìn)程進(jìn)行通信時,能否實(shí)現(xiàn)進(jìn)程間的對象傳送呢?當(dāng)然是可以的!如何做到呢?這就需要使用Java序列化與反序列化了。發(fā)送方需要把這個Java對象轉(zhuǎn)換為字節(jié)序列,然后在網(wǎng)絡(luò)上傳輸,接收方則需要將字節(jié)序列中恢復(fù)出Java對象。
我們清楚了為什么需要使用Java序列化和反序列化后,我們很自然地會想到Java序列化有哪些好處:
實(shí)現(xiàn)了數(shù)據(jù)的持久化,通過序列化可以把數(shù)據(jù)永久地保存到硬盤上(如:存儲在文件里),實(shí)現(xiàn)永久保存對象。 利用序列化實(shí)現(xiàn)遠(yuǎn)程通信,即:能夠在網(wǎng)絡(luò)上傳輸對象。
JavaPub參考巨人:https://xcbeyond.blog.csdn.net/article/details/100046212
3.動態(tài)代理是什么?有哪些應(yīng)用?
動態(tài)代理:在運(yùn)行時,創(chuàng)建目標(biāo)類,可以調(diào)用和擴(kuò)展目標(biāo)類的方法。
Java 中實(shí)現(xiàn)動態(tài)的方式:JDK 中的動態(tài)代理 和 Java類庫 CGLib。
應(yīng)用場景如:
統(tǒng)計每個 api 的請求耗時
統(tǒng)一的日志輸出
校驗(yàn)被調(diào)用的 api 是否已經(jīng)登錄和權(quán)限鑒定
Spring的 AOP 功能模塊就是采用動態(tài)代理的機(jī)制來實(shí)現(xiàn)切面編程
JavaPub參考巨人:https://www.cnblogs.com/aheizi/p/4861422.html
4.怎么實(shí)現(xiàn)動態(tài)代理?
Java領(lǐng)域中,常用的動態(tài)代理實(shí)現(xiàn)方式有兩種,一種是利用JDK反射機(jī)制生成代理,另外一種是使用CGLIB代理。
JDK代理必須要提供接口,而CGLIB則不需要,可以直接代理類。下面分別舉例說明。
1.JDK動態(tài)代理:
public?interface?People?{
????public?void?sayHello();
}public?class?Chinese?implements?People?{
????
????public?void?sayHello()?{
????????System.out.println("Chinese?say?hello.");
????}
}import?java.lang.reflect.InvocationHandler;
import?java.lang.reflect.Method;
public?class?PeopleInvocationHandler?implements?InvocationHandler{
????
????private?Object?peolple;
????
????Intermediary(Object?people){
????????this.people?=?people;
????}
????
????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)
????????????throws?Throwable?{????????Object?invoke?=?method.invoke(people,?args);
????????System.out.println("--------?end?---------");
????????return?invoke;
????}
}import?java.lang.reflect.Proxy;
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????People?chinese?=?new?People();
????????PeopleInvocationHandler?invocationHandler?=?new?PeopleInvocationHandler(chinese);
????????People?proxy?=?(People)?Proxy.newProxyInstance(chinese.getClass().getClassLoader(),?chinese.getClass().getInterfaces(),?invocationHandler);
????????proxy.sayHello();
????}
}
2.CGLIB動態(tài)代理
需要引入CGLIB相關(guān)Jar包
public?class?Chinese?{
????public?void?sayHello(){
????????System.out.println("Chinese?say?hello");
????}
}import?java.lang.reflect.Method;
import?net.sf.cglib.proxy.MethodInterceptor;
import?net.sf.cglib.proxy.MethodProxy;
public?class?ChinesePoxy?implements?MethodInterceptor?{
????
????public?Object?intercept(Object?object,?Method?method,?Object[]?args,MethodProxy?methodProxy)?throws?Throwable?{
????????Object?intercept?=?methodProxy.invokeSuper(object,?args);
?????System.out.println("--------?end?---------");
????return?intercept;?
??}?
}import?net.sf.cglib.proxy.Enhancer;
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????ChineseProxy?chineseProxy?=?new?ChineseProxy();
????????
????????Enhancer?enhancer?=?new?Enhancer();??
????????enhancer.setSuperclass(Chinese.class);
????????enhancer.setCallback(chineseProxy);
????????
????????Chinese?proxy?=?(Chinese)?enhancer.create();
????????proxy.sayHello();
????}
}
JavaPub參考巨人:https://www.cnblogs.com/xifengxiaoma/p/9377261.html
如何使用Java的反射?
通過一個全限類名創(chuàng)建一個對象
Class.forName(“全限類名”); 例如:com.mysql.jdbc.Driver Driver類已經(jīng)被加載到 jvm中,并且完成了類的初始化工作就行了
類名.class; 獲取Class<?> clz 對象
對象.getClass();
獲取構(gòu)造器對象,通過構(gòu)造器new出一個對象
Clazz.getConstructor([String.class]);
Con.newInstance([參數(shù)]);
通過class對象創(chuàng)建一個實(shí)例對象(就相當(dāng)與new類名()無參構(gòu)造器)
Cls.newInstance();
通過class對象獲得一個屬性對象
Field c=cls.getFields():獲得某個類的所有的公共(public)的字段,包括父類中的字段。
Field c=cls.getDeclaredFields():獲得某個類的所有聲明的字段,即包括public、private和proteced,但是不包括父類的聲明字段
通過class對象獲得一個方法對象
Cls.getMethod(“方法名”,class……parameaType);(只能獲取公共的)
Cls.getDeclareMethod(“方法名”);(獲取任意修飾的方法,不能執(zhí)行私有)
M.setAccessible(true);(讓私有的方法可以執(zhí)行)
讓方法執(zhí)行
Method.invoke(obj實(shí)例對象,obj可變參數(shù));-----(是有返回值的)
2021 面試題,認(rèn)準(zhǔn) JavaPub。