Java 反射,這篇寫的很透徹!

Java技術(shù)棧
www.javastack.cn
關(guān)注閱讀更多優(yōu)質(zhì)文章
一、反射機(jī)制是什么?
二、反射的具體使用
2.1 獲取對(duì)象的包名以及類名
2.2 獲取Class對(duì)象
2.3 getInstance()獲取指定類型的實(shí)例化對(duì)象
2.4 通過構(gòu)造函數(shù)對(duì)象實(shí)例化對(duì)象
2.5 獲取類繼承的接口
2.6 獲取父類相關(guān)信息
2.7 獲取當(dāng)前類的公有屬性和私有屬性以及更新
2.8 獲取以及調(diào)用類的公有/私有方法
三、反射的優(yōu)缺點(diǎn)
3.1 優(yōu)點(diǎn)
3.2 缺點(diǎn)
很多時(shí)候我們會(huì)遇到別人問一個(gè)問題:你給我講一下反射,到底是什么東西?怎么實(shí)現(xiàn)的?我們能用反射來做什么?它有什么優(yōu)缺點(diǎn)?下面我們會(huì)圍繞著這幾個(gè)問題展開:
一、反射機(jī)制是什么?
反射是什么?什么是反?什么是正射?有反就有正,我們知道正常情況, 如果我們希望創(chuàng)建一個(gè)對(duì)象,會(huì)使用以下的語句:
Person?person?=?new?Person();
其實(shí)我們第一次執(zhí)行上面的語句的時(shí)候,JVM會(huì)先加載Person.class,加載到內(nèi)存完之后,在方法區(qū)/堆中會(huì)創(chuàng)建了一個(gè)Class對(duì)象,對(duì)應(yīng)這個(gè)Person類。這里有爭(zhēng)議,有人說是在方法區(qū),有些人說是在堆。個(gè)人感覺應(yīng)該JVM規(guī)范說是在方法區(qū),但是不是強(qiáng)制要求,而且不同版本的JVM實(shí)現(xiàn)也不一樣。具體參考以下鏈接,這里不做解釋:https://www.cnblogs.com/xy-nb/p/6773051.html
? ?而上面正常的初始化對(duì)象的方法,也可以說是“正射”,就是使用Class對(duì)象創(chuàng)建出一個(gè)Person對(duì)象。
而反射則相反,是根據(jù)Person對(duì)象,獲取到Class對(duì)象,然后可以獲取到Person類的相關(guān)信息,進(jìn)行初始化或者調(diào)用等一系列操作。
在運(yùn)行狀態(tài)時(shí),可以構(gòu)造任何一個(gè)類的對(duì)象,獲取到任意一個(gè)對(duì)象所屬的類信息,以及這個(gè)類的成員變量或者方法,可以調(diào)用任意一個(gè)對(duì)象的屬性或者方法。可以理解為具備了動(dòng)態(tài)加載對(duì)象以及對(duì)對(duì)象的基本信息進(jìn)行剖析和使用的能力。
提供的功能包括:
1.在運(yùn)行時(shí)判斷一個(gè)對(duì)象所屬的類 2.在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象 3.在運(yùn)行時(shí)獲取一個(gè)類定義的成員變量以及方法 4.在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法 5.生成動(dòng)態(tài)代理
靈活,強(qiáng)大,可以在運(yùn)行時(shí)裝配,無需在組件之間進(jìn)行源代碼鏈接,但是使用不當(dāng)效率會(huì)有影響。所有類的對(duì)象都是Class的實(shí)例。既然我們可以對(duì)類的全限定名,方法以及參數(shù)等進(jìn)行配置,完成對(duì)象的初始化,那就是相當(dāng)于增加了java的可配置性。
這里特別需要明確的一點(diǎn):類本身也是一個(gè)對(duì)象,方法也是一個(gè)對(duì)象,在Java里面萬物皆可對(duì)象,除了基礎(chǔ)數(shù)據(jù)類型...
二、反射的具體使用
2.1 獲取對(duì)象的包名以及類名
package?invocation;
public?class?MyInvocation?{
????public?static?void?main(String[]?args)?{
????????getClassNameTest();
????}
????
????public?static?void?getClassNameTest(){
????????MyInvocation?myInvocation?=?new?MyInvocation();
????????System.out.println("class:?"?+?myInvocation.getClass());
????????System.out.println("simpleName:?"?+?myInvocation.getClass().getSimpleName());
????????System.out.println("name:?"?+?myInvocation.getClass().getName());
????????System.out.println("package:?"?+
????????????????""?+?myInvocation.getClass().getPackage());
????}
}
運(yùn)行結(jié)果:
class:?class?invocation.MyInvocation
simpleName:?MyInvocation
name:?invocation.MyInvocation
package:?package?invocation
由上面結(jié)果我們可以看到:1.getClass():打印會(huì)帶著class+全類名
? ?2.getClass().getSimpleName():只會(huì)打印出類名
? ?3.getName():會(huì)打印全類名
? ?4.getClass().getPackage():打印出package+包名
getClass()獲取到的是一個(gè)對(duì)象,getPackage()也是。
2.2 獲取Class對(duì)象
在java中,一切皆對(duì)象。java中可以分為兩種對(duì)象,實(shí)例對(duì)象和Class對(duì)象。這里我們說的獲取Class對(duì)象,其實(shí)就是第二種,Class對(duì)象代表的是每個(gè)類在運(yùn)行時(shí)的類型信息,指和類相關(guān)的信息。比如有一個(gè)Student類,我們用Student student = new Student()new一個(gè)對(duì)象出來,這個(gè)時(shí)候Student這個(gè)類的信息其實(shí)就是存放在一個(gè)對(duì)象中,這個(gè)對(duì)象就是Class類的對(duì)象,而student這個(gè)實(shí)例對(duì)象也會(huì)和Class對(duì)象關(guān)聯(lián)起來。我們有三種方式可以獲取一個(gè)類在運(yùn)行時(shí)的Class對(duì)象,分別是
Class.forName("com.Student") student.getClass() Student.class
實(shí)例代碼如下:
package?invocation;
public?class?MyInvocation?{
????public?static?void?main(String[]?args)?{
????????getClassTest();
????}
????public?static?void?getClassTest(){
????????Class>?invocation1?=?null;
????????Class>?invocation2?=?null;
????????Class>?invocation3?=?null;
????????try?{
????????????//?最常用的方法
????????????invocation1?=?Class.forName("invocation.MyInvocation");
????????}catch?(Exception?ex){
????????????ex.printStackTrace();
????????}
????????invocation2?=?new?MyInvocation().getClass();
????????invocation3?=?MyInvocation.class;
????????System.out.println(invocation1);
????????System.out.println(invocation2);
????????System.out.println(invocation3);
????}
}
執(zhí)行的結(jié)果如下,三個(gè)結(jié)果一樣:
class?invocation.MyInvocation
class?invocation.MyInvocation
class?invocation.MyInvocation
2.3 getInstance()獲取指定類型的實(shí)例化對(duì)象
首先我們有一個(gè)Student類,后面都會(huì)沿用這個(gè)類,將不再重復(fù)。
class?Student{
????private?int?age;
????private?String?name;
????public?Student()?{
????}
????public?Student(int?age)?{
????????this.age?=?age;
????}
????public?Student(String?name)?{
????????this.name?=?name;
????}
????
????public?Student(int?age,?String?name)?{
????????this.age?=?age;
????????this.name?=?name;
????}
????public?int?getAge()?{
????????return?age;
????}
????public?void?setAge(int?age)?{
????????this.age?=?age;
????}
????public?String?getName()?{
????????return?name;
????}
????public?void?setName(String?name)?{
????????this.name?=?name;
????}
????@Override
????public?String?toString()?{
????????return?"Student{"?+
????????????????"age="?+?age?+
????????????????",?name='"?+?name?+?'\''?+
????????????????'}';
????}
我們可以使用getInstance()方法構(gòu)造出一個(gè)Student的對(duì)象:
????public?static?void?getInstanceTest()?{
????????try?{
????????????Class>?stduentInvocation?=?Class.forName("invocation.Student");
????????????Student?student?=?(Student)?stduentInvocation.newInstance();
????????????student.setAge(9);
????????????student.setName("Hahs");
????????????System.out.println(student);
????????}catch?(Exception?ex){
????????????ex.printStackTrace();
????????}
????}
????
????
輸出結(jié)果如下:
Student{age=9,?name='Hahs'}
但是如果我們?nèi)∠粚慡tudent的無參構(gòu)造方法呢?就會(huì)出現(xiàn)下面的報(bào)錯(cuò):
java.lang.InstantiationException:?invocation.Student
?at?java.lang.Class.newInstance(Class.java:427)
?at?invocation.MyInvocation.getInstanceTest(MyInvocation.java:40)
?at?invocation.MyInvocation.main(MyInvocation.java:8)
Caused?by:?java.lang.NoSuchMethodException:?invocation.Student.()
?at?java.lang.Class.getConstructor0(Class.java:3082)
?at?java.lang.Class.newInstance(Class.java:412)
?...?2?more
這是因?yàn)槲覀冎貙懥藰?gòu)造方法,而且是有參構(gòu)造方法,如果不寫構(gòu)造方法,那么每個(gè)類都會(huì)默認(rèn)有無參構(gòu)造方法,重寫了就不會(huì)有無參構(gòu)造方法了,所以我們調(diào)用newInstance()的時(shí)候,會(huì)報(bào)沒有這個(gè)方法的錯(cuò)誤。值得注意的是,newInstance()是一個(gè)無參構(gòu)造方法。
2.4 通過構(gòu)造函數(shù)對(duì)象實(shí)例化對(duì)象
除了newInstance()方法之外,其實(shí)我們還可以通過構(gòu)造函數(shù)對(duì)象獲取實(shí)例化對(duì)象,怎么理解?這里只構(gòu)造函數(shù)對(duì)象,而不是構(gòu)造函數(shù),也就是構(gòu)造函數(shù)其實(shí)就是一個(gè)對(duì)象,我們先獲取構(gòu)造函數(shù)對(duì)象,當(dāng)然也可以使用來實(shí)例化對(duì)象。
可以先獲取一個(gè)類的所有的構(gòu)造方法,然后遍歷輸出:
????public?static?void?testConstruct(){
????????try?{
????????????Class>?stduentInvocation?=?Class.forName("invocation.Student");
????????????Constructor>?cons[]?=?stduentInvocation.getConstructors();
????????????for(int?i=0;i????????????????System.out.println(cons[i]);
????????????}
????????}catch?(Exception?ex){
????????????ex.printStackTrace();
????????}
????}
輸出結(jié)果:
public?invocation.Student(int,java.lang.String)
public?invocation.Student(java.lang.String)
public?invocation.Student(int)
public?invocation.Student()
取出一個(gè)構(gòu)造函數(shù)我們可以獲取到它的各種信息,包括參數(shù),參數(shù)個(gè)數(shù),類型等等:
????public?static?void?constructGetInstance()?{
????????try?{
????????????Class>?stduentInvocation?=?Class.forName("invocation.Student");
????????????Constructor>?cons[]?=?stduentInvocation.getConstructors();
????????????Constructor?constructors?=?cons[0];
????????????System.out.println("name:?"?+?constructors.getName());
????????????System.out.println("modifier:?"?+?constructors.getModifiers());
????????????System.out.println("parameterCount:?"?+?constructors.getParameterCount());
????????????System.out.println("構(gòu)造參數(shù)類型如下:");
????????????for?(int?i?=?0;?i?????????????????System.out.println(constructors.getParameterTypes()[i].getName());
????????????}
????????}?catch?(Exception?ex)?{
????????????ex.printStackTrace();
????????}
????}
輸出結(jié)果,modifier是權(quán)限修飾符,1表示為public,我們可以知道獲取到的構(gòu)造函數(shù)是兩個(gè)參數(shù)的,第一個(gè)是int,第二個(gè)是String類型,看來獲取出來的順序并不一定是我們書寫代碼的順序。
name:?invocation.Student
modifier:?1
parameterCount:?2
構(gòu)造參數(shù)類型如下:
int
java.lang.String
既然我們可以獲取到構(gòu)造方法這個(gè)對(duì)象了,那么我們可不可以通過它去構(gòu)造一個(gè)對(duì)象呢?答案肯定是可以?。。?/strong>下面我們用不同的構(gòu)造函數(shù)來創(chuàng)建對(duì)象:
????public?static?void?constructGetInstanceTest()?{
????????try?{
????????????Class>?stduentInvocation?=?Class.forName("invocation.Student");
????????????Constructor>?cons[]?=?stduentInvocation.getConstructors();
????????????//?一共定義了4個(gè)構(gòu)造器
????????????Student?student1?=?(Student)?cons[0].newInstance(9,"Sam");
????????????Student?student2?=?(Student)?cons[1].newInstance("Sam");
????????????Student?student3?=?(Student)?cons[2].newInstance(9);
????????????Student?student4?=?(Student)?cons[3].newInstance();
????????????System.out.println(student1);
????????????System.out.println(student2);
????????????System.out.println(student3);
????????????System.out.println(student4);
????????}?catch?(Exception?ex)?{
????????????ex.printStackTrace();
????????}
輸出如下:
Student{age=9,?name='Sam'}
Student{age=0,?name='Sam'}
Student{age=9,?name='null'}
Student{age=0,?name='null'}
構(gòu)造器的順序我們是必須一一針對(duì)的,要不會(huì)報(bào)一下的參數(shù)不匹配的錯(cuò)誤:
java.lang.IllegalArgumentException:?argument?type?mismatch
?at?sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native?Method)
?at?sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
?at?sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
?at?java.lang.reflect.Constructor.newInstance(Constructor.java:423)
?at?invocation.MyInvocation.constructGetInstanceTest(MyInvocation.java:85)
?at?invocation.MyInvocation.main(MyInvocation.java:8)
2.5 獲取類繼承的接口
通過反射我們可以獲取接口的方法,如果我們知道某個(gè)類實(shí)現(xiàn)了接口的方法,同樣可以做到通過類名創(chuàng)建對(duì)象調(diào)用到接口的方法。
首先我們定義兩個(gè)接口,一個(gè)InSchool:
public?interface?InSchool?{
????public?void?attendClasses();
}
一個(gè)AtHome:
public?interface?AtHome?{
????public?void?doHomeWork();
}
創(chuàng)建一個(gè)實(shí)現(xiàn)兩個(gè)接口的類Student.java
public?class?Student?implements?AtHome,?InSchool?{
????public?void?doHomeWork()?{
????????System.out.println("I?am?a?student,I?am?doing?homework?at?home");
????}
????public?void?attendClasses()?{
????????System.out.println("I?am?a?student,I?am?attend?class?in?school");
????}
}
測(cè)試代碼如下:
public?class?Test?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????Class>?studentClass?=?Class.forName("invocation.Student");
????????Class>[]?interfaces?=?studentClass.getInterfaces();
????????for?(Class?c?:?interfaces)?{
????????????//?獲取接口
????????????System.out.println(c);
????????????//?獲取接口里面的方法
????????????Method[]?methods?=?c.getMethods();
????????????//?遍歷接口的方法
????????????for?(Method?method?:?methods)?{
????????????????//?通過反射創(chuàng)建對(duì)象
????????????????Student?student?=?(Student)?studentClass.newInstance();
????????????????//?通過反射調(diào)用方法
????????????????method.invoke(student,?null);
????????????}
????????}
????}
}
結(jié)果如下:
可以看出其實(shí)我們可以獲取到接口的數(shù)組,并且里面的順序是我們繼承的順序,通過接口的Class對(duì)象,我們可以獲取到接口的方法,然后通過方法反射調(diào)用實(shí)現(xiàn)類的方法,因?yàn)檫@是一個(gè)無參數(shù)的方法,所以只需要傳null即可。
2.6 獲取父類相關(guān)信息
主要是使用getSuperclass()方法獲取父類,當(dāng)然也可以獲取父類的方法,執(zhí)行父類的方法,首先創(chuàng)建一個(gè)Animal.java:
public?class?Animal?{
????public?void?doSomething(){
????????System.out.println("animal?do?something");
????}
}
Dog.java繼承于Animal.java:
public?class?Dog?extends?Animal{
????public?void?doSomething(){
????????System.out.println("Dog?do?something");
????}
}
我們可以通過反射創(chuàng)建Dog對(duì)象,獲取其父類Animal以及創(chuàng)建對(duì)象,當(dāng)然也可以獲取Animal的默認(rèn)父類Object:
public?class?Test?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????Class>?dogClass?=?Class.forName("invocation02.Dog");
????????System.out.println(dogClass);
????????invoke(dogClass);
????????Class>?animalClass?=?dogClass.getSuperclass();
????????System.out.println(animalClass);
????????invoke(animalClass);
????????Class>?objectClass?=?animalClass.getSuperclass();
????????System.out.println(objectClass);
????????invoke(objectClass);
????}
????public?static?void?invoke(Class>?myClass)?throws?Exception?{
????????Method[]?methods?=?myClass.getMethods();
????????//?遍歷接口的方法
????????for?(Method?method?:?methods)?{
????????????if?(method.getName().equalsIgnoreCase("doSomething"))?{
????????????????//?通過反射調(diào)用方法
????????????????method.invoke(myClass.newInstance(),?null);
????????????}
????????}
????}
}
輸入如下:
2.7 獲取當(dāng)前類的公有屬性和私有屬性以及更新
創(chuàng)建一個(gè)Person.java,里面有靜態(tài)變量,非靜態(tài)變量,以及public,protected,private不同修飾的屬性。
public?class?Person?{
????public?static?String?type?;
????private?static?String?subType?;
????//?名字(公開)
????public?String?name;
????protected?String?gender;
????private?String?address;
????@Override
????public?String?toString()?{
????????return?"Person{"?+
????????????????"name='"?+?name?+?'\''?+
????????????????",?address='"?+?address?+?'\''?+
????????????????'}';
????}
}
使用getFields()可以獲取到public的屬性,包括static屬性,使用getDeclaredFields()可以獲取所有聲明的屬性,不管是public,protected,private不同修飾的屬性。
修改public屬性,只需要field.set(object,value)即可,但是private屬性不能直接set,否則會(huì)報(bào)以下的錯(cuò)誤。
Exception?in?thread?"main"?java.lang.IllegalAccessException:?Class?invocation03.Tests?can?not?access?a?member?of?class?invocation03.Person?with?modifiers?"private"
?at?sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
?at?java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
?at?java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
?at?java.lang.reflect.Field.set(Field.java:761)
?at?invocation03.Tests.main(Tests.java:21)
那么需要怎么做呢?private默認(rèn)是不允許外界操作其值的,這里我們可以使用field.setAccessible(true);,相當(dāng)于打開了操作的權(quán)限。
那static的屬性修改和非static的一樣,但是我們?cè)趺传@取呢?如果是public修飾的,可以直接用類名獲取到,如果是private修飾的,那么需要使用filed.get(object),這個(gè)方法其實(shí)對(duì)上面說的所有的屬性都可以的。測(cè)試代碼如下
public?class?Tests?{
????public?static?void?main(String[]?args)?throws?Exception{
????????Class>?personClass?=?Class.forName("invocation03.Person");
????????Field[]?fields?=?personClass.getFields();
????????//?獲取公開的屬性
????????for(Field?field:fields){
????????????System.out.println(field);
????????}
????????System.out.println("=================");
????????//?獲取所有聲明的屬性
????????Field[]?declaredFields?=?personClass.getDeclaredFields();
????????for(Field?field:declaredFields){
????????????System.out.println(field);
????????}
????????System.out.println("=================");
????????Person?person?=?(Person)?personClass.newInstance();
????????person.name?=?"Sam";
????????System.out.println(person);
????????//?修改public屬性
????????Field?fieldName?=?personClass.getDeclaredField("name");
????????fieldName.set(person,"Jone");
????????//?修改private屬性
????????Field?addressName?=?personClass.getDeclaredField("address");
????????//?需要修改權(quán)限
????????addressName.setAccessible(true);
????????addressName.set(person,"東風(fēng)路47號(hào)");
????????System.out.println(person);
????????//?修改static?靜態(tài)public屬性
????????Field?typeName?=?personClass.getDeclaredField("type");
????????typeName.set(person,"人類");
????????System.out.println(Person.type);
????????//?修改靜態(tài)?private屬性
????????Field?subType?=?personClass.getDeclaredField("subType");
????????subType.setAccessible(true);
????????subType.set(person,"黃種人");
????????System.out.println(subType.get(person));
????}
}
結(jié)果:
從結(jié)果可以看出,不管是public,還是protected,private修飾的,我們都可以通過反射對(duì)其進(jìn)行查詢和修改,不管是靜態(tài)變量還是非靜態(tài)變量。getDeclaredField()可以獲取到所有聲明的屬性,而getFields()則只能獲取到public的屬性。對(duì)于非public的屬性,我們需要修改其權(quán)限才能訪問和修改:field.setAccessible(true)。
獲取屬性值需要使用field.get(object),值得注意的是:每個(gè)屬性,其本身就是對(duì)象
2.8 獲取以及調(diào)用類的公有/私有方法
既然可以獲取到公有屬性和私有屬性,那么我想,執(zhí)行公有方法和私有方法應(yīng)該都不是什么問題?
那下面我們一起來學(xué)習(xí)一下...
先定義一個(gè)類,包含各種修飾符,以及是否包含參數(shù),是否為靜態(tài)方法,Person.java:
public?class?Person?{
????//?非靜態(tài)公有無參數(shù)
????public?void?read(){
????????System.out.println("reading...");
????}
????//?非靜態(tài)公有無參數(shù)有返回
????public?String?getName(){
????????return?"Sam";
????}
????//?非靜態(tài)公有帶參數(shù)???
????public?int?readABookPercent(String?name){
????????System.out.println("read?"+name);
????????return?80;
????}
????//?私有有返回值
????private?String?getAddress(){
????????return?"東方路";
????}
????//?公有靜態(tài)無參數(shù)無返回值
????public?static?void?staticMethod(){
????????System.out.println("static?public?method");
????}
????//?公有靜態(tài)有參數(shù)
????public?static?void?staticMethodWithArgs(String?args){
????????System.out.println("static?public?method:"+args);
????}
????//?私有靜態(tài)方法
????private?static?void?staticPrivateMethod(){
????????System.out.println("static?private?method");
????}
}
首先我們來看看獲取里面所有的方法:
public?class?Tests?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????Class>?personClass?=?Class.forName("invocation03.Person");
????????Method[]?methods?=?personClass.getMethods();
????????for?(Method?method?:?methods)?{
????????????System.out.println(method);
????????}
????????System.out.println("=============================================");
????????Method[]?declaredMethods?=?personClass.getDeclaredMethods();
????????for?(Method?method?:?declaredMethods)?{
????????????System.out.println(method);
????????}
????}
}
結(jié)果如下:
咦,我們發(fā)現(xiàn)getMethods()確實(shí)可以獲取所有的公有的方法,但是有一個(gè)問題,就是他會(huì)把父類的也獲取到,也就是上面圖片綠色框里面的,我們知道所有的類默認(rèn)都繼承了Object類,所以它把Object的那些方法都獲取到了。而getDeclaredMethods確實(shí)可以獲取到公有和私有的方法,不管是靜態(tài)還是非靜態(tài),但是它是獲取不到父類的方法的。
那如果我們想調(diào)用方法呢?先試試調(diào)用非靜態(tài)方法:
public?class?Tests?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????Class>?personClass?=?Class.forName("invocation03.Person");
????????Person?person?=?(Person)?personClass.newInstance();
????????Method[]?declaredMethods?=?personClass.getDeclaredMethods();
????????for?(Method?method?:?declaredMethods)?{
????????????if(method.getName().equalsIgnoreCase("read")){
????????????????method.invoke(person,null);
????????????????System.out.println("===================");
????????????}else?if(method.getName().equalsIgnoreCase("getName")){
????????????????System.out.println(method.invoke(person,null));
????????????????System.out.println("===================");
????????????}else?if(method.getName().equalsIgnoreCase("readABookPercent")){
????????????????System.out.println(method.invoke(person,"Sam"));
????????????????System.out.println("===================");
????????????}
????????}
????}
}
結(jié)果如下,可以看出method.invoke(person,null);是調(diào)用無參數(shù)的方法,而method.invoke(person,"Sam")則是調(diào)用有參數(shù)的方法,要是有更多參數(shù),也只需要在里面多加一個(gè)參數(shù)即可,返回值也同樣可以獲取到。
那么private方法呢?我們照著來試試,試試就試試,who 怕 who?
public?class?Tests?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????Class>?personClass?=?Class.forName("invocation03.Person");
????????Person?person?=?(Person)?personClass.newInstance();
????????Method[]?declaredMethods?=?personClass.getDeclaredMethods();
????????for?(Method?method?:?declaredMethods)?{
????????????if(method.getName().equalsIgnoreCase("getAddress")){
????????????????method.invoke(person,null);
????????????}
????????}
????}
}
結(jié)果報(bào)錯(cuò)了:
Exception?in?thread?"main"?java.lang.IllegalAccessException:?Class?invocation03.Tests?can?not?access?a?member?of?class?invocation03.Person?with?modifiers?"private"
?at?sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
?at?java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
?at?java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
?at?java.lang.reflect.Method.invoke(Method.java:491)
?at?invocation03.Tests.main(Tests.java:13)

一看就是沒有權(quán)限,小場(chǎng)面,不要慌,我來操作一波,只要加上
method.setAccessible(true);
哦豁,完美解決了...
那么問題來了,上面說的都是非靜態(tài)的,我就想要調(diào)用靜態(tài)的方法。當(dāng)然用上面的方法,對(duì)象也可以直接調(diào)用到類的方法的:
一點(diǎn)問題都沒有,為什么輸出結(jié)果有幾個(gè)null,那是因?yàn)檫@函數(shù)是無返回值的呀,笨蛋...
如果我不想用遍歷方法的方式,再去判斷怎么辦?能不能直接獲取到我想要的方法?。磕谴鸢缚隙ㄊ强梢园?。
public?class?Tests?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????Class>?personClass?=?Class.forName("invocation03.Person");
????????Person?person?=?(Person)?personClass.newInstance();
????????Method?method?=?personClass.getMethod("readABookPercent",?String.class);
????????method.invoke(person,?"唐詩三百首");
????}
}
結(jié)果和上面調(diào)用的完全一樣,圖我就不放了,就一行字。要是這個(gè)方法沒有參數(shù)呢?那就給一個(gè)null就可以啦?;蛘卟唤o也可以。
public?class?Tests?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????Class>?personClass?=?Class.forName("invocation03.Person");
????????Person?person?=?(Person)?personClass.newInstance();
????????Method?method?=?personClass.getMethod("getName",null);
????????System.out.println(method.invoke(person));
????}
}
三、反射的優(yōu)缺點(diǎn)
3.1 優(yōu)點(diǎn)
反射可以在不知道會(huì)運(yùn)行哪一個(gè)類的情況下,獲取到類的信息,創(chuàng)建對(duì)象以及操作對(duì)象。這其實(shí)很方便于拓展,所以反射會(huì)是框架設(shè)計(jì)的靈魂,因?yàn)榭蚣茉谠O(shè)計(jì)的時(shí)候,為了降低耦合度,肯定是需要考慮拓展等功能的,不能將類型寫死,硬編碼。
降低耦合度,變得很靈活,在運(yùn)行時(shí)去確定類型,綁定對(duì)象,體現(xiàn)了多態(tài)功能。
3.2 缺點(diǎn)
這么好用,沒有缺點(diǎn)?怎么可能?。?!有利就有弊,事物都是有雙面性的。即使功能很強(qiáng)大,但是反射是需要?jiǎng)討B(tài)類型的,JVM沒有辦法優(yōu)化這部分代碼,執(zhí)行效率相對(duì)直接初始化對(duì)象較低。一般業(yè)務(wù)代碼不建議使用。
反射可以修改權(quán)限,比如上面訪問到private這些方法和屬性,這是會(huì)破壞封裝性的,有安全隱患,有時(shí)候,還會(huì)破壞單例的設(shè)計(jì)。
反射會(huì)使代碼變得復(fù)雜,不容易維護(hù),畢竟代碼還是要先寫給人看的嘛,逃~






關(guān)注Java技術(shù)??锤喔韶?/strong>


