Java基礎(chǔ)回歸之反射Reflection

反射是什么鬼

反射其實(shí)就是允許我們獲取目標(biāo)類的方法、成員變量等信息,以及可以調(diào)用、改變某些方法和成員變量的值。( JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法;這種動(dòng)態(tài)獲取的以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為Java的反射機(jī)制。)--莎士比亞

詳細(xì)見(jiàn)某百科反射百科,這個(gè)不是重點(diǎn),下面開(kāi)始反射之旅。

反射的使用

先上一張反射相關(guān)知識(shí)點(diǎn)的腦圖,如果對(duì)腦圖上東西一竅不通或者有些模糊,那么請(qǐng)往下看。

反射Reflection.png
  • 反射機(jī)制相關(guān)的類

  • java.lang.Class; //類

  • java.lang.reflect.Constructor;//構(gòu)造方法

  • java.lang.reflect.Field; //類的成員變量

  • java.lang.reflect.Method;//類的方法

  • java.lang.reflect.Modifier;//訪問(wèn)權(quán)限

  • java.lang.reflect.Parameter;//方法參數(shù)

  • Classclass的區(qū)別

  • class:小寫(xiě)字母c開(kāi)頭的class是聲明一個(gè)類的關(guān)鍵字。

  • Class:在Java中,反射的源頭就是Class,每個(gè)class都有一個(gè)相應(yīng)的Class對(duì)象。也就是說(shuō),當(dāng)我們編寫(xiě)一個(gè)類,編譯完成后,在生成的.class文件中,就會(huì)產(chǎn)生一個(gè)Class對(duì)象,它封裝了這個(gè)類的信息,包括上面說(shuō)的反射機(jī)制相關(guān)的類等信息。

  • Class的獲取
    上面說(shuō)了,Class類用于封裝類的各種信息,它是反射的源頭,獲取一個(gè)類的Class通常有以下三種方法:

  • Class cls = A.class;

  • A a = new A();
    Class cls = a.getClass();

  • Class cls = Class.forName("com.xx.A");

三種方法獲取Class.png
  • 通過(guò)獲取到的Class生成一個(gè)實(shí)例對(duì)象
    A a = (A) cls.newInstance(); //獲取A的實(shí)例對(duì)象
    假設(shè)我們有一個(gè)Car類,里面有一個(gè)start()方法,我們可以以下方法得到Car類的實(shí)例并調(diào)用start()方法,注意,需要無(wú)參構(gòu)造函數(shù):

    反射獲取Car實(shí)例.png

  • 類信息的獲取

獲取類的方法API 說(shuō)明
Method[] methods = cls.getDeclaredMethods() 獲取當(dāng)前類聲明的所有方法列表(無(wú)視權(quán)限修飾符)
Method[] methods = cls.getMethods() 獲取當(dāng)前類及父類所有public方法列表
Method method = cls.getDeclaredMethod(name, parameterTypes) 獲取當(dāng)前類聲明的某個(gè)特定方法
Method method = cls.getMethod(name, parameterTypes) 獲取當(dāng)前類或父類某個(gè)特定public方法
獲取類的成員變量API 說(shuō)明
Field[] fields = cls.getDeclaredFields() 獲取當(dāng)前類聲明的所有成員變量列表(無(wú)視權(quán)限修飾符)
Field[] fields = cls.getFields() 獲取當(dāng)前類及父類所有public成員變量列表
Field field = cls.getDeclaredField(name) 獲取當(dāng)前類聲明的某個(gè)特定成員變量
Field field = cls.getField(name) 獲取當(dāng)前類或父類某個(gè)特定public成員變量
獲取類的構(gòu)造函數(shù)API 說(shuō)明
Constructor[] constructors = cls.getDeclaredConstructors() 獲取當(dāng)前類聲明的所有成員變量列表(無(wú)視權(quán)限修飾符)
Constructor[] constructors = cls.getConstructors() 獲取當(dāng)前類及父類所有public構(gòu)造函數(shù)列表
Constructor constructor = cls.getDeclaredConstructor(parameterTypes) 獲取當(dāng)前類聲明的某個(gè)特定構(gòu)造函數(shù)
Constructor constructor = cls.getConstructor(parameterTypes) 獲取當(dāng)前類或父類某個(gè)特定public構(gòu)造函數(shù)
獲取類或方法的修飾符API 說(shuō)明
int modifiers = cls.getModifiers() 獲取類的修飾符,注意返回值是int類型,可通過(guò)Modifier.toString(modifiers)獲取到相對(duì)應(yīng)的String類型
int modifiers = method.getModifiers() 獲取方法的修飾符,同上

修飾符返回值int類型對(duì)應(yīng)的修飾符如下圖:

訪問(wèn)修飾符.png
獲取方法的參數(shù)API 說(shuō)明
Class[] parameterTypes =method.getParameterTypes() 獲取方法參數(shù)的類類型列表(即.class,如int.class)
Parameter[] parameters = method.getParameters() 獲取方法參數(shù)列表(即.class,如int.class)
獲取編程元素上的注解API 說(shuō)明
Annotation[] annotations = cls(/method/field).getAnnotations() 獲取類/方法/成員變量上標(biāo)注的所有注解
Annotation[] declaredAnnotations = cls(/method/field).getDeclaredAnnotations() 獲取類/方法/成員變量上標(biāo)注的所有注解
Override annotation = cls(/method/field).getAnnotation(Override.class) 獲取類/方法/成員變量上某個(gè)特定注解
Annotation[][] parameterAnnotations = m.getParameterAnnotations() 獲取方法參數(shù)上的注解
boolean b = cls(/method/field).isAnnotationPresent(Bind.class) 該變成元素上是否有Bind.class注解

getDeclaredAnnotation(s):返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒(méi)有注釋直接存在于此元素上,則返回長(zhǎng)度為零的一個(gè)數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響。
getAnnotation(s):返回此元素上存在的所有注釋。(如果此元素沒(méi)有注釋,則返回長(zhǎng)度為零的數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響。
getDeclaredAnnotations得到的是當(dāng)前成員所有的注釋,不包括繼承的。而getAnnotations得到的是包括繼承的所有注釋。
關(guān)鍵在于繼承的問(wèn)題上,getDeclaredAnnotations和getAnnotations是否相同,就在于父類的注解是否可繼承,這可以用sun.reflect.annotation.AnnotationType antype3=AnnotationType.getInstance(Class.forName(annotationtype_class(example:"javax.ejb.Stateful")).isInherited())來(lái)判定,如果為true,說(shuō)明可以被繼承則存在與getAnnotations之中而不在getDeclaredAnnotations之中,否則,也不存在與getannnotations中,因?yàn)椴荒鼙焕^承。

  • 通過(guò)反射獲取類中某個(gè)方法并調(diào)起
//我們有這個(gè)Car類
public class Car {
    @Override
    public void start() {
        System.out.println("Car start!");
    }
}
Car car = new Car();
Class cls = car.getClass();//獲取類類型
Method method = cls.getDeclaredMethod("start", new Class[]{});//獲取start方法,第一個(gè)參數(shù)為方法名字,  
//第二個(gè)參數(shù)是一個(gè)可變參數(shù),接收的是方法的參數(shù)的類類型列表,此處Car里面有一個(gè)方法是無(wú)參的start(),則傳入new Class[]{};同理,假設(shè)Car類還有一個(gè)重載的start(int speed)方法,則我們應(yīng)該通過(guò)cls.getDeclaredMethod("start", new Class[]{int.class});
//method.setAccessible(true);//注意如果目標(biāo)方法是private修飾的,則需要先調(diào)用setAccessible(true)破封裝,此處Car類里的start方法是public,所以不需要調(diào)用
Object obj = method.invoke(car, new Class[]{});//傳入的和上面的獲取方法參數(shù)同理,此處不說(shuō)。  
//需要說(shuō)明的是如果方法沒(méi)有返回值,則返回是null,即我們這里接收到的obj是null;如果對(duì)應(yīng)方法有返回值則返回具體的返回值。
  • 通過(guò)反射獲取類中某個(gè)成員變量并修改它的值
//1.獲取intField成員變量
Field declaredField = cls.getDeclaredField("intField");
//2.如果該成員變量是private,此時(shí)我們應(yīng)該先設(shè)置為可操作
declaredField.setAccessible(true);
//3.賦值
declaredField.set(ca, 5);```

## 舉個(gè)栗子
下面通過(guò)一個(gè)栗子把上面所說(shuō)反射的使用全部串聯(lián)起來(lái)。需求:打印類中所有信息,并修改某個(gè)成員變量。具體看注釋,并且跟著手打一遍印象深刻點(diǎn),這里直接上代碼。

首先我們聲明一個(gè)父類:BaseActivity
```java
public class BaseActivity {
    
    public int basePublicFiled;//父類公有成員變量
    private String basePrivateFiled;//父類私有成員變量
    
    private BaseActivity(){
        //父類私有構(gòu)造函數(shù)
    }
    
    public BaseActivity(int i){
        //父類公有構(gòu)造函數(shù)
    }
    
    public void basePublicMethod(String str){
        //父類公有方法
    }
    
    private void basePrivateMethod(String str){
        //父類私有方法
    }
}

然后聲明一個(gè)ChildActivity繼承它:

public class ChildActivity extends BaseActivity{

    public ChildActivity(int i) {
        super(i);
    }
    
    public int childPublicFiled;
    private String childPrivateFiled;
    
    public void childPublicMethod(String str){
        //no-op
    }
    
    private void childPrivateMethod(String str){
        //no-op
    }
    
    private int intField = 0;//注意,這里值是0,待會(huì)反射修改這個(gè)成員變量的值
    public void plus(int i){
        //待會(huì)反射調(diào)用此方法,注意看原始值和反射后打印出來(lái)的結(jié)果
        System.out.println(String.valueOf(i) +" + "+ String.valueOf(intField) + " = " + (i + intField));
    }
    
}

編寫(xiě)核心類,取名ClassMessageGetter,用于獲取反射操作

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

import entity.ChildActivity;

public class ClassMessageGetter {

    /**
     * 獲取某個(gè)類類信息
     * @param obj
     */
    public static void getMethodMessage(Object obj){
        
        //獲取方法
        //1.getMethods()獲取該類所有public,包括父類的public方法
        //2.getDeclaredMethods()獲取當(dāng)前類(不含父類)的所有方法(與方法訪問(wèn)權(quán)限無(wú)關(guān))
        System.out.println("1.getMethods()獲取該類所有public,包括父類的public方法\n");
        Class cls = obj.getClass();
        Method[] methods = cls.getMethods();
        Method[] declaredMethods = cls.getDeclaredMethods();
        for(Method method : methods){
            //通過(guò)method.getReturnType().getName()獲取返回值名稱
            //通過(guò)method.getName()獲取方法名稱
            int modifiers = cls.getModifiers();
            System.out.print(Modifier.toString(method.getModifiers())+" "+method.getReturnType().getName() + " " + method.getName()+"(");
            //通過(guò)method.getParameterTypes();獲取方法參數(shù)的類類型
            Class[] parameterTypes = method.getParameterTypes();
            //通過(guò)method.getParameters()獲取方法參數(shù)
            Parameter[] parameters = method.getParameters();
            for(int  i = 0 ; i < parameterTypes.length ; i ++){
                System.out.print(parameterTypes[i].getSimpleName() + " " +parameters[i].getName()+ (i!=parameterTypes.length-1?",":""));
            }
            System.out.println(")");
        }
        
        System.out.println("\n-----------------------------------------------------------\n");
        System.out.println("2.getDeclaredMethods()獲取當(dāng)前類(不含父類)的所有方法(與方法訪問(wèn)權(quán)限無(wú)關(guān))\n");
        
        for(Method method : declaredMethods){
            System.out.println(method.getName());
        }
        
    }
    
    /**
     * 獲取類成員變量信息
     * @param obj
     */
    public static void getFieldMessage(Object obj){
        Class cls = obj.getClass();
        //1.getFields()獲取該類所有public,包括父類的public成員變量
        Field[] fields = cls.getFields();
        //2.getDeclaredFields()獲取當(dāng)前類(不含父類)的所有成員變量(與變量訪問(wèn)權(quán)限無(wú)關(guān))
        Field[] declaredFields = cls.getDeclaredFields();
        System.out.println("cls.getFields() 獲取該類所有public,包括父類的public成員變量");
        for (Field field : fields) {
            System.out.println(field.getType().getSimpleName() + " "+field.getName());
        }
        
        System.out.println("\ngetDeclaredFields()獲取當(dāng)前類(不含父類)的所有成員變量(與變量訪問(wèn)權(quán)限無(wú)關(guān))");
        for (Field field : declaredFields) {
            System.out.println(field.getType().getSimpleName() + " "+field.getName());
        }
    }
    
    /**
     * 獲取構(gòu)造函數(shù)信息
     * @param obj
     */
    public static void getConstructorMessage(Object obj){
        Class cls = obj.getClass();
        Constructor[] constructors = cls.getConstructors();
        Constructor[] declaredConstructors = cls.getDeclaredConstructors();
        
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName());
        }
        
        System.out.println("------------------");
        for (Constructor constructor : declaredConstructors) {
            System.out.println(constructor.getName());
        }
        
        
    }
    
    
    /**
     * 通過(guò)反射操作方法和成員變量
     */
    public  static void reflectionProcess(){
        
        ChildActivity ca = new ChildActivity(0);
        Class cls = ca.getClass();
        try {
            //修改filed值
            //1.獲取intField成員變量
            Field declaredField = cls.getDeclaredField("intField");
            //2.因?yàn)樵摮蓡T變量是private,此時(shí)我們應(yīng)該先設(shè)置為可操作
            declaredField.setAccessible(true);
            //3.賦值
            declaredField.set(ca, 5);
            
            //反射調(diào)用方法
            //1.獲取目標(biāo)方法plus(int i)
            Method method  = cls.getDeclaredMethod("plus", int.class);
            //因?yàn)樵摲椒ㄊ莗ublic的,因此不需要調(diào)用 method.setAccessible(true);
            //2.調(diào)用method.invoke(對(duì)象,參數(shù)列表)
            method.invoke(ca, 6);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

最后寫(xiě)一個(gè)測(cè)試類Demain進(jìn)行測(cè)試:

public class Domain {

    public static void main(String[] args) {
        System.out.println("===================方法信息===================");
        ClassMessageGetter.getMethodMessage(new ChildActivity(0));
        System.out.println("===================成員信息===================");
        ClassMessageGetter.getFieldMessage(new ChildActivity(0));
        System.out.println("===================構(gòu)造函數(shù)信息===================");
        ClassMessageGetter.getConstructorMessage(new ChildActivity(0));
        System.out.println("===================修改成員變量及調(diào)用方法信息===================");
        ClassMessageGetter.reflectionProcess();
    }
}

打印結(jié)果如下:

===================方法信息===================
1.getMethods()獲取該類所有public,包括父類的public方法

public void plus(int arg0)
public void childPublicMethod(String arg0)
public void basePublicMethod(String arg0)
public final void wait()
public final void wait(long arg0,int arg1)
public final native void wait(long arg0)
public boolean equals(Object arg0)
public java.lang.String toString()
public native int hashCode()
public final native java.lang.Class getClass()
public final native void notify()
public final native void notifyAll()

-----------------------------------------------------------

2.getDeclaredMethods()獲取當(dāng)前類(不含父類)的所有方法(與方法訪問(wèn)權(quán)限無(wú)關(guān))

plus
childPublicMethod
childPrivateMethod
===================成員信息===================
cls.getFields() 獲取該類所有public,包括父類的public成員變量
int childPublicFiled
int basePublicFiled

getDeclaredFields()獲取當(dāng)前類(不含父類)的所有成員變量(與變量訪問(wèn)權(quán)限無(wú)關(guān))
int childPublicFiled
String childPrivateFiled
int intField
===================構(gòu)造函數(shù)信息===================
entity.ChildActivity
------------------
entity.ChildActivity
===================修改成員變量及調(diào)用方法信息===================
6 + 5 = 11

到此,Java反射基本就完結(jié)。最后說(shuō)一個(gè)與反射相關(guān)的東西,叫“類型擦除”,它和泛型有關(guān)。

泛型是1.5中引入的一個(gè)新的概念,由于不用進(jìn)行強(qiáng)制轉(zhuǎn)換類型了,所以具有較高的安全性和易用性。因?yàn)榉盒推鋵?shí)只是在編譯器中實(shí)現(xiàn)的而虛擬機(jī)并不認(rèn)識(shí)泛型類項(xiàng),所以要在虛擬機(jī)中將泛型類型進(jìn)行擦除。也就是說(shuō),在編譯階段使用泛型,運(yùn)行階段取消泛型,即擦除。

就是說(shuō)泛型機(jī)制其實(shí)是在編譯期進(jìn)行“檢查”,因此,只要我們能夠繞過(guò)編譯期,就可以為所欲為。先看下面代碼:

泛型檢查.png

可以看到,當(dāng)我們add進(jìn)去的類型不是String時(shí),編譯器就會(huì)報(bào)錯(cuò),那么有什么辦法可以add進(jìn)去int類型的值嗎?上面說(shuō)了,泛型機(jī)制是在編譯期進(jìn)行的,所以我們可以通過(guò)反射,add進(jìn)去其他類型的值。
先看下面代碼:
list類類型比較.png

從上面的代碼我們可以知道,兩個(gè)list的Class其實(shí)是同一份,從側(cè)面印證了泛型機(jī)制是在編譯期進(jìn)行的。接下來(lái)即將發(fā)生神奇的事情:

類型擦除.png

最終結(jié)果size打印的是3,說(shuō)明我們成功地往貓碗(List<String>)里面撒了狗糧(int)。

The End

轉(zhuǎn)載請(qǐng)注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在經(jīng)過(guò)一次沒(méi)有準(zhǔn)備的面試后,發(fā)現(xiàn)自己雖然寫(xiě)了兩年的android代碼,基礎(chǔ)知識(shí)卻忘的差不多了。這是程序員的大忌,沒(méi)...
    猿來(lái)如癡閱讀 3,111評(píng)論 3 10
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 34,626評(píng)論 18 399
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,030評(píng)論 0 9
  • 和往常一樣,在天黑透的時(shí)候,趙英回到了租住的地方。 左手拎著冷掉的包子,右手攥著新出爐的報(bào)紙,想起雜亂的屋子,第三...
    逯曉風(fēng)閱讀 480評(píng)論 0 1
  • 2016年10月6日 鑲珠花瓣凝水滴, 秋風(fēng)輕拂潤(rùn)膚肌, 千樹(shù)綠褪葉漸黃, 惟我猶自著彩衣。
    葉知秋yzq閱讀 197評(píng)論 0 0

友情鏈接更多精彩內(nèi)容