新聞中心
書(shū)寫(xiě)代碼必須符合高質(zhì)量高性能要求,這也是能夠在視覺(jué)上和其他程序員拉開(kāi)差距的技能,同時(shí)也是一個(gè)優(yōu)秀程序員的基本要求。

創(chuàng)新互聯(lián)公司長(zhǎng)期為近1000家客戶(hù)提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為河曲企業(yè)提供專(zhuān)業(yè)的成都網(wǎng)站建設(shè)、成都做網(wǎng)站,河曲網(wǎng)站改版等技術(shù)服務(wù)。擁有十載豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
- 何為高質(zhì)量:代碼具備可維護(hù)性,可讀性,可擴(kuò)展性,靈活性,簡(jiǎn)潔性,可復(fù)用性, 可測(cè)試性。
- 何為高性能:代碼能盡可能的提高處理效率。
今天我們說(shuō)一說(shuō)反射,反射不是設(shè)計(jì)模式,但是反射機(jī)制作為java的基礎(chǔ)之一,在眾多框架的源碼中大量使用,是很多設(shè)計(jì)模式,框架,組件的重要基礎(chǔ)。比如我們知道的spring aop底層是jdk的動(dòng)態(tài)代理,而動(dòng)態(tài)代理依賴(lài)的就是反射機(jī)制。
一、反射機(jī)制
1.概念
在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類(lèi),都能知道這個(gè)類(lèi)的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象都能調(diào)用它的任意方法和屬性,這種動(dòng)態(tài)獲取類(lèi)信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱(chēng)為Java的發(fā)射機(jī)制。
先了解下類(lèi)對(duì)象的概念:
我們知道類(lèi)的加載過(guò)程為加載 驗(yàn)證 準(zhǔn)備 解析 初始化 銷(xiāo)毀,在加載階段,jvm會(huì)根據(jù)類(lèi)的全限定名找到二進(jìn)制字節(jié)流,并把這個(gè)二進(jìn)制字節(jié)流加載進(jìn)內(nèi)存,轉(zhuǎn)為運(yùn)行時(shí)數(shù)據(jù)區(qū)的存儲(chǔ)結(jié)構(gòu),最后創(chuàng)建一個(gè)java.lang.Class類(lèi)型的實(shí)例作為方法區(qū)這個(gè)類(lèi)型的訪問(wèn)入口,這里所說(shuō)的Class實(shí)例就是類(lèi)對(duì)象,這個(gè)類(lèi)對(duì)象創(chuàng)建完成后會(huì)存放在堆區(qū),這個(gè)動(dòng)作是在加載階段完成。
方法區(qū)中存放的是類(lèi)的元數(shù)據(jù),包括靜態(tài)變量,有哪些屬性,有哪些方法,繼承的父類(lèi),實(shí)現(xiàn)的接口,異常相關(guān)的信息等等,而類(lèi)對(duì)象就是這些信息的訪問(wèn)入口,Class類(lèi)提供了很多api,這些api大多是native方法,也就說(shuō)明這個(gè)類(lèi)對(duì)象只是這個(gè)類(lèi)在堆區(qū)的一個(gè)接口,由jvm底層來(lái)實(shí)現(xiàn),jvm底層會(huì)根據(jù)每個(gè)api的功能去方法區(qū)拿類(lèi)的信息。
2.反射的應(yīng)用
public static void main(String[] args) {
UserService userService = new UserService();
System.out.println("new關(guān)鍵字創(chuàng)建對(duì)象:"+userService);
Class userClass= UserService.class;
Class userClass1= Class.forName("UserService");
Constructor>[] constructors=userClass.getDeclaredConstructors();
Constructor constructor=constructors[0];
Object user=constructor.newInstance("333333","666666");
System.out.println("反射創(chuàng)建對(duì)象:"+user;
} 通過(guò)上面的這個(gè)例子,我們可以看到反射是如何應(yīng)用的:
- 首先通過(guò).class或者Class.forName()方法,獲取一個(gè)Class實(shí)例,這個(gè)實(shí)例就是類(lèi)對(duì)象,
- 然后通過(guò)調(diào)用這個(gè)Class實(shí)例的方法獲取類(lèi)的構(gòu)造方法,得到構(gòu)造方法Constructor實(shí)例
- 然后調(diào)用Constructor實(shí)例的newInstance方法進(jìn)行實(shí)例化對(duì)象。
以上是利用反射機(jī)制創(chuàng)建對(duì)象,當(dāng)然除了創(chuàng)建對(duì)象,還可以獲取類(lèi)的屬性實(shí)例Field和方法實(shí)例Method,通過(guò)方法實(shí)例和屬性實(shí)例的api對(duì)對(duì)象的方法和屬性進(jìn)行設(shè)置或者執(zhí)行。這便是反射的應(yīng)用。
3.反射的特點(diǎn)
new關(guān)鍵字創(chuàng)建對(duì)象是加載類(lèi)完成后接著走創(chuàng)建對(duì)象過(guò)程,而反射過(guò)程是Class.forName()觸發(fā)加載類(lèi),但是不會(huì)創(chuàng)建對(duì)象,只有在調(diào)用newInstance方法時(shí)候才會(huì)創(chuàng)建對(duì)象,也就是把加載類(lèi)和創(chuàng)建對(duì)象分為兩個(gè)部分完成,但是調(diào)用newInstance方法創(chuàng)建前必須保證類(lèi)已經(jīng)加載完成。
new關(guān)鍵字創(chuàng)建對(duì)象是靜態(tài)編譯,而反射創(chuàng)建對(duì)象是動(dòng)態(tài)編譯:
- 靜態(tài)編譯:在編譯的時(shí)候就已經(jīng)知道要?jiǎng)?chuàng)建什么對(duì)象,就會(huì)把對(duì)應(yīng)類(lèi)加載(忽略懶加載)
- 動(dòng)態(tài)編譯:在編譯的時(shí)候不知道要?jiǎng)?chuàng)建什么對(duì)象,等到運(yùn)行到這段代碼的時(shí)候才知道要?jiǎng)?chuàng)建什么對(duì)象。
比如下面的代碼,編譯階段是不知道是否要?jiǎng)?chuàng)建UserService類(lèi)的對(duì)象的,所以UserService不會(huì)被加載:
public void reflex(String str) {
if("UserService".equals(str)){
Class userClass= Class.forName("UserService");
Constructor>[] constructors=userClass.getDeclaredConstructors();
Constructor constructor=constructors[0];
Object UserService=constructor.newInstance("333333","666666");
System.out.println(UserService.toString());
}
}以java8為討論基礎(chǔ),網(wǎng)上所說(shuō)的反射只能通過(guò)無(wú)參構(gòu)造方法創(chuàng)建對(duì)象是不正確的,事實(shí)證明,反射不僅僅可以通過(guò)有參構(gòu)造方式創(chuàng)建對(duì)象,而且還可以通過(guò)私有構(gòu)造方法創(chuàng)建對(duì)象,而且這種通過(guò)私有構(gòu)造方法創(chuàng)建對(duì)象的方式會(huì)破壞單例模式,你想一下,單例模式中的構(gòu)造方法之所以是私有就是為了不允許外部創(chuàng)建單例對(duì)象。而通過(guò)反射可以創(chuàng)建的話(huà),那不是違背了單例模式的定理嗎。
例:只是為了說(shuō)明反射,所有代碼中的單例只是一個(gè)簡(jiǎn)單的餓漢式單例:
public class IdGenerator {
private String k;
private static final IdGenerator instance = new IdGenerator();
private IdGenerator(String k) {
this.k=k;
}
public static IdGenerator getInstance() {
return instance;
}
}
public class reflex {
public static void main(String[] args){
Class idGeneratorClass= Class.forName("IdGenerator");
Constructor>[] constructors=idGeneratorClass.getDeclaredConstructors();
Constructor constructor=constructors[0];
constructor.setAccessible(true);//暴力反射,可以突破私有權(quán)限
Object idGenerator=constructor.newInstance("333333");
System.out.println(idGenerator==IdGenerator.getInstance());
}
}這個(gè)例子既驗(yàn)證了反射調(diào)用有參構(gòu)造方法創(chuàng)建實(shí)例,又驗(yàn)證了反射破壞單例模式。
反射會(huì)造成泛型擦除:
List list=new ArrayList();
list.add(new UserService());
list.add(new UserService());
list.add(new UserService());
Class extends List> listClass=list.getClass();
Method method=listClass.getDeclaredMethod("add",Object.class);
method.invoke(list,"123");
for(int i=0;i 上面的例子中通過(guò)反射創(chuàng)建的list對(duì)象,在調(diào)用add方法的時(shí)候不會(huì)限制類(lèi)型,導(dǎo)致無(wú)法用某個(gè)類(lèi)型去接收l(shuí)ist集合中數(shù)據(jù),否則會(huì)報(bào)類(lèi)型轉(zhuǎn)換異常,這種情況只能直接返回前端。
效率問(wèn)題 ,反射的效率比new字段創(chuàng)建對(duì)象的效率低很多,因此在使用的時(shí)候要特別注意性能問(wèn)題,但是即便是這樣,我們寫(xiě)出的代碼主要的性能影響點(diǎn)很少是反射造成,而大多情況是因?yàn)榇a結(jié)構(gòu)框架,函數(shù),工具,底層原理的不合理使用造成的。因此發(fā)射機(jī)制可以用在代碼中,但是要用在合適的位置。
現(xiàn)在來(lái)總結(jié)下反射的作用:
可以在程序運(yùn)行過(guò)程中去操作字節(jié)碼文件和類(lèi)對(duì)象進(jìn)而進(jìn)行得到類(lèi)信息,創(chuàng)建對(duì)象以及執(zhí)行對(duì)象方法等,不需要重新編譯,提高程序的擴(kuò)展性 復(fù)用性 解耦
二、反射相關(guān)的四個(gè)類(lèi)
反射相關(guān)的四個(gè)類(lèi),這些類(lèi)的中的方法底層大多是native方法,所以反射其實(shí)是jvm底層實(shí)現(xiàn)。掌握了這四個(gè)類(lèi),靈活運(yùn)用,基本就掌握了反射機(jī)制。
1.Class類(lèi)
- getClassLoader() 返回類(lèi)加載器
- getClasses() 返回一個(gè)數(shù)組,該數(shù)組中包含該類(lèi)中所有公共類(lèi)和接口類(lèi)的類(lèi)對(duì)象
- getDeclaredClasses() 返回一個(gè)數(shù)組,數(shù)組中包含該類(lèi)中所有類(lèi)的和接口類(lèi)的對(duì)象
- forName(String className) 根據(jù)類(lèi)名返回類(lèi)的類(lèi)對(duì)象
- newInstance()創(chuàng)建類(lèi)的實(shí)例
- getPackage()獲取類(lèi)的包
- getSimpleName() 獲取類(lèi)的名字
- getSuperclass() 獲取當(dāng)前類(lèi)繼承的類(lèi)的名字
- getInterfaces() 獲取當(dāng)前類(lèi)實(shí)現(xiàn)的類(lèi)或者接口
- .class 獲取當(dāng)前對(duì)象的類(lèi)對(duì)象
- getField(String str) 獲取public的字段對(duì)象 只能得到public
- getFields() 獲取所有public字段對(duì)象 只能得到public
- getDeclaredFild(String name) 取某個(gè)字段對(duì)象
- getDeclaredFilds() 取所有字段對(duì)象
- getAnnotation(Class) 獲取注解
- getConstructor(String.calss...) 獲取該類(lèi)中對(duì)應(yīng)參數(shù)類(lèi)型的構(gòu)造方法
- getConstructors()獲取該類(lèi)的所有公有構(gòu)造方法
- getDeclaredConstructor(String.calss...)獲取該類(lèi)中與參數(shù)類(lèi)型匹配的構(gòu)造方法
- getDeclaredConstructors()獲取所有構(gòu)造方法
- getMethods()獲取該類(lèi)所有公有方法
- getMethod(String name,String.calss...) 獲取該類(lèi)對(duì)應(yīng)名稱(chēng)和參數(shù)類(lèi)型的公有方法
- getDeclaredMethods() 獲取所有方法
- getDeclaredMethods(String name,String.calss...)獲取該類(lèi)對(duì)應(yīng)名稱(chēng)和參數(shù)類(lèi)型的方法
- isAnnotation()如果是注解類(lèi)型返回true
- isnotationPresent(注解類(lèi)型)如果是指定類(lèi)型的注解返回true
- isArray()如果是數(shù)組類(lèi)型返回true
- isEnum()如果是枚舉類(lèi)型 返回true
- isInstance(Object obj)如果傳入的參數(shù)是該類(lèi)的實(shí)例則返回true
- isInterface()如果是接口類(lèi)型返回true
2.Field類(lèi)
field是類(lèi)中的成員變量:變量和屬性是倆個(gè)概念,變量有g(shù)et和set方法就是屬性。
- get(Object obj)獲取obj對(duì)象中對(duì)應(yīng)的屬性值
- set(Object obj,Object val)設(shè)置obj對(duì)象中對(duì)應(yīng)屬性值
- setAccessible() 暴力反射,忽略訪問(wèn)權(quán)限修飾符
3.Method類(lèi)
- invoke(Object obj,object args...) 入對(duì)象及參數(shù)調(diào)用該對(duì)象的該方法
- getName() 取某個(gè)方法的名字
- setAccessible() 暴力反射,忽略訪問(wèn)權(quán)限修飾符
4.Constructor類(lèi)
newInstance(object arg...) 傳入?yún)?shù)的時(shí)候,會(huì)調(diào)用有對(duì)應(yīng)參數(shù)的構(gòu)造方法創(chuàng)建對(duì)象,不傳參數(shù)就是使用默認(rèn)構(gòu)造方法。
setAccessible() 暴力反射,忽略訪問(wèn)權(quán)限修飾符:
Class userClass= Class.forName("UserService");
Constructor>[] constructors=userClass.getDeclaredConstructors();
Constructor constructor=constructors[0];
Object UserService=constructor.newInstance("333333","666666"); 所謂暴力反射,就是當(dāng)類(lèi)中有私有構(gòu)造方法,私有屬性,私有方法的時(shí)候,對(duì)這些對(duì)象進(jìn)行反射調(diào)用的時(shí)候會(huì)報(bào)錯(cuò),原因是無(wú)法突破私有權(quán)限,反射調(diào)用前先調(diào)用對(duì)象的setAccessible方法,設(shè)置為true,就可以突破私有權(quán)限,代碼可以看上面破壞單例的例子。
名稱(chēng)欄目:你真的了解Java的反射機(jī)制嗎?
文章起源:http://m.fisionsoft.com.cn/article/cocijio.html


咨詢(xún)
建站咨詢(xún)
