Class類與反射

舉個例子,把代碼過程看作去一個目標(biāo)地點,普通代碼調(diào)用呢就是事先知道經(jīng)緯度,然后你坐直升機(jī)直接就到了;而通過反射呢就像不知道具體的地點,只知道先去一個地點,然后前往下一個地點,一步步到達(dá)目標(biāo)。這兩種方法殊途同歸,反射因為要“尋路”,所以會慢一些,但在找到目標(biāo)地點后和直接調(diào)用是一樣的。

有時候我們需要在程序中創(chuàng)建新的對象或是調(diào)用一個方法,而對應(yīng)的細(xì)節(jié)我們事先并不知道,也就是說要在運行中動態(tài)地獲得類的信息和調(diào)用方法。下面介紹如何利用反射來實現(xiàn)。

Class類:保存和類有關(guān)的信息的類

  • 需要了解的概念

    • RTTI(RunTime Type Information,運行時類型信息)能夠在程序運行時發(fā)現(xiàn)和使用類型信息

    • Class對象就保存著運行時類型信息RTTI,表示一個特定類的屬性

    • Class類實際上表示的是一個泛型類Class<?>

    • 當(dāng)編譯一個新類時JVM會調(diào)用類加載器把這個類加載到內(nèi)存中

      • 類加載器首先會檢查這個類的 Class 對象是否已經(jīng)加載,如果尚未加載,默認(rèn)的類加載器就會根據(jù)類名查找 .class 文件

      • 一旦某個類的 Class 對象被載入內(nèi)存,它就可以用來創(chuàng)建這個類的所有對象

    • JVM為每個類型管理一個Class對象

  • 利用Class類得到類的信息和實例化一個類

    • Class.getName()方法

      • 返回類的名字,如果類在一個包中還會加上包名

      • 使用getSimpleName()得到不帶包名的類名

    • Class.forName(String className)方法

      • 獲得className類名對應(yīng)的Class對象
    • Object.newInstance()方法

      • 可以用來動態(tài)地創(chuàng)建一個類的實例

      • 方法調(diào)用類的默認(rèn)構(gòu)造器(沒有參數(shù)的構(gòu)造器),如果沒有默認(rèn)構(gòu)造器,就會拋出一個異常

      • 如果要調(diào)用帶參數(shù)的構(gòu)造器,使用Constructor類中的newInstance方法

    • 例程

      • 代碼

        System.out.println("靜態(tài)創(chuàng)建一個新的對象");
        Employee ae = new Employee();
        System.out.println(ae);
        //使用getClass.getName得到類名
        String cName = ae.getClass().getName();
        //再用forName和newInstance動態(tài)創(chuàng)建一個對象
        System.out.println("getClass()+forName()動態(tài)創(chuàng)建一個新的對象");
        Object o = Class.forName(cName).newInstance();
        System.out.println(o.getClass());//會輸出實際類型Employee
        System.out.println(o);
        
        //先定義一個類名 再新建對象
        String m = "CoreJava.c5_inheritance.Manager";
        System.out.println("先定義再forName()動態(tài)創(chuàng)建一個新的對象");
        Object am = Class.forName(m).newInstance();
        System.out.println(am.getClass());//會輸出實際類型Manager
        System.out.println(am);
        
        
      • 結(jié)果


        image

反射:分析類的能力,相對動態(tài)地執(zhí)行一些方法

  • 利用Field類查看任意對象的數(shù)據(jù)域名稱和類型

    • 首先獲得要分析類的Class對象getClass()/forName()

    • getField(String fieldName) 和 getFileds() 能獲取Class對象的對應(yīng)域(getDeclared(), getDeclaredFields()獲取所有已聲明域,包括私有域)

    • Field.getName()能獲取域名稱,F(xiàn)ield.getType()能獲取域類型

    • 例程

      • 代碼

        System.out.println("利用反射獲得所有域");
        Manager manager = new Manager();
        Class clazz = manager.getClass();//先得到類的運行時信息
        Field[] fields = clazz.getDeclaredFields();//獲得所有聲明的域(包括私有域)
        for (Field f : fields)
        System.out.println(f.getType() + " " + f.getName());
        
        
      • 結(jié)果


        image
  • 獲得域中的值并修改

    • Field.get(Object obj)

      • 可以獲得obj對象中用Filed對象表示的域值(設(shè) f 是Field的一個實例,表示Manager類中的salary域,那么f.get(m)可以獲得Manager實例m中salary域的值)

      • 如果是私有域,需要先設(shè)置可訪問標(biāo)志為true : fild.setAccessible(true)

    • Field.set(object obj, Object newValue)

      • 用一個新值設(shè)置obj對象中Field對象表示的域
    • 例程

      • 代碼

        //獲得域中的值并修改
        System.out.println("利用反射獲得域中的值并修改");
        System.out.println("使用類方法getSalary():" + manager.getSalary());
        Field managerSalaryField = clazz.getDeclaredField("salary");//注意異常處理
        managerSalaryField.setAccessible(true);//設(shè)置可訪問標(biāo)志為true,訪問私有域
        System.out.println("使用反射獲得salary:" + managerSalaryField.get(manager));//注意異常處理
        System.out.println("使用反射修改salary");
        managerSalaryField.set(manager, 999);
        System.out.println("修改后salary:" + manager.getSalary());
        
        
      • 結(jié)果


        image
  • 利用Method類獲得任意方法名稱和返回值

    • Class.getMethod(String methodName, Class<?>[] paramTypes) 和 Class.getMethods() 分別能獲得類的對應(yīng)Method對象和所有Method對象

    • Method.getName()獲得方法名

    • Methord.getReturnType()獲得方法返回類型

    • 例程

      • 代碼

        System.out.println("利用反射獲得所有方法");
        Method[] methods = clazz.getMethods();
        for (Method method : methods)
            System.out.println(method.getReturnType().getSimpleName() + " " + method.getName() + " ");
        
        
      • 結(jié)果


        image
  • 調(diào)用任意方法

    • Method.invoke(Object obj, Object... params)可以調(diào)用obj對象中Method對象表示的方法,params是方法參數(shù)。對于靜態(tài)方法第一個參數(shù)可以傳入null

    • 例程

      • 代碼

        System.out.println("利用反射調(diào)用任意方法");
        System.out.println("修改前salary:" + manager.getSalary());
        Method setManagerSalaryMethod = clazz.getMethod("setSalary", int.class);
        setManagerSalaryMethod.invoke(manager, 222);//第一個參數(shù)為執(zhí)行對象,靜態(tài)方法可傳入null
        System.out.println("修改后salary:" + manager.getSalary());
        
        
      • 結(jié)果


        image

我的疑問

  • 利用反射能獲取甚至修改類的私有域,這樣一來私有域不就沒有意義了嗎。在網(wǎng)上搜了一些資料,感覺還是沒有明白,等到理解了再來補(bǔ)上。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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