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

        共 17245字,需瀏覽 35分鐘

         ·

        2020-11-23 23:27

        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)變量,以及publicprotected,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,還是protectedprivate修飾的,我們都可以通過反射對(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>



        戳原文,獲取精選面試題!
        瀏覽 36
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
            
            

                      • 国产精品白丝jk白祙喷水网站 | 日本少妇激情舌吻 | 日韩午夜精品视频 | 精品人妻一区二区蜜桃视频 | 成人黄网站 免费入口 |