国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

理解反射的概念

2019-11-14 22:42:59
字體:
來源:轉載
供稿:網友
理解反射的概念一、反射
學員馮偉立(大二輟學,現廣州電信)聽完反射后的一句話:“反射就是把java類中的各種成分映射成相應的java類”。這句話比許多書上講的都透徹,都精辟! 我們先學完這些反射API后,后面再通過一個綜合案例來說明反射API的價值和作用。

 

  • 反射就是把Java類中的各種成分映射成相應的Java類。例如,一個Java類中用一個Class類的對象來表示,一個類中的組成部分:成員變量,方法,構造方法,包等等信息也用一個個的Java類來表示,就像汽車是一個類,汽車中的發動機,變速箱等等也是一個個的類。表示java的Class類顯然要提供一系列的方法,來獲得其中的變量,方法,構造方法,修飾符,包等信息,這些信息就是用相應類的實例對象來表示,它們是Field、Method、Contructor、Package等等。

 

  • 一個類中的每個成員都可以用相應的反射API類的一個實例對象來表示,通過調用Class類的方法可以得到這些實例對象后,得到這些實例對象后有什么用呢?怎么用呢?這正是學習和應用反射的要點。

 

二、反射API 2.1 Constructor類
  • Constructor類代表某個類的一個構造方法
    • 得到某個類所有的構造方法:

                   例子:Constructor constructors[]=Class.forName("java.lang.String").getConstructors();

    • 得到某一個構造方法:

                           例子:Constructor constructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);

    • 創建實例對象:
      • 通常方式: String s=new String(new StringBuffer("abc"));
      • 反射方式:

                              Constructor constructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);                               String ss=(java.lang.String)constructor.newInstance(/*"abc"*/new StringBuffer("abc"));

編譯器只看代碼的編譯,不看代碼的執行,我們開發人員一定要分清楚錯誤出現的不同階段。

    • Class.newInstance()為創建一個對象提供了便利
      • 之前 class—constructor—>new obj
      • 現在提供了最簡捷的方式: Class實例.newInstance()--默認調用無參構造

                       如:Class.forName(("java.lang.String").newInstance();

      • 該方法內部先得到默認的構造方法,然后用該構造方法創建實例對象。

 

 //java.lang.Class源碼:
public T newInstance()         throws InstantiationException, IllegalaccessException    {    if (System.getSecurityManager() != null) {            checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), false);    }    return newInstance0();    }    PRivate T newInstance0()        throws InstantiationException, IllegalAccessException    {        // NOTE: the following code may not be strictly correct under        // the current Java memory model.        // Constructor lookup        if (cachedConstructor == null) {//01.有緩存機制,說明得到某個類的構造器的字節碼是比較消耗資源的            if (this == Class.class) {                throw new IllegalAccessException(                    "Can not call newInstance() on the Class for java.lang.Class"                );            }            try {        Class[] empty = {};                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);                // Disable accessibility checks on the constructor                // since we have to do the security check here anyway                // (the stack depth is wrong for the Constructor's                // security check to work)                java.security.AccessController.doPrivileged                    (new java.security.PrivilegedAction() {                            public Object run() {                                c.setAccessible(true);                                return null;                            }                        });                cachedConstructor = c;//02.把字節碼的構造器給緩存起來            } catch (NoSuchMethodException e) {                throw new InstantiationException(getName());            }        }        Constructor<T> tmpConstructor = cachedConstructor;//03.把緩存構造器交給臨時變量        // Security check (same as in java.lang.reflect.Constructor) 安全檢查        int modifiers = tmpConstructor.getModifiers();        if (!Reflection.quickCheckMemberAccess(this, modifiers)) {            Class caller = Reflection.getCallerClass(3);            if (newInstanceCallerCache != caller) {                Reflection.ensureMemberAccess(caller, this, null, modifiers);                newInstanceCallerCache = caller;            }        }        // Run constructor        try {            return tmpConstructor.newInstance((Object[])null);//04.調用構造器,返回該類實例        } catch (InvocationTargetException e) {            Unsafe.getUnsafe().throwException(e.getTargetException());            // Not reached            return null;        }    }
//源碼中之所以緩存構造器,是因為得到構造器的過程是比較消耗資源的。

 

 

 

2.2 Field類
  • Field類代表某個類中的一個成員變量
  • 演示用eclipse自動身份Java類的構造方法
  • 問題:得到的Field對象時對應到類上面的成員變量,還是對應到對象上的成員變量?類只有一個,而該類的實例對象有多個,如果是與對相關聯,那關聯的是哪個對象呢?所以字段filedX代表的是x的定義,而不是具體的x變量。
package com.itcast.day1;public class ReflectPoint {     //x是私有的, 01.要使用 字節碼.getDeclaredField("x") 獲得x的定義fieldX.     //02.使用fieldX.setAccessible(true) 來進行暴力破解private訪問權限。     private int x;   //y是共有的,用getField("y")就可以,也不涉及暴力破解權限。    public int y;//共有的         public ReflectPoint(int x, int y) {         super();         this.x = x;         this.y = y;     } }

 

/***main方法中的測試代碼******成員變量的反射*********/         ReflectPoint rp=new ReflectPoint(2, 4);         Field fieldY=rp.getClass().getDeclaredField("y");         //fieldY 值是多少? 4嗎?錯!         //fieldY不是對象身上的變量,而是類上,要用它去取某個對象身上對應的值         System.out.println(fieldY.get(rp));//用fieldY去取對象rp上對應的變量y的值。         //     Field fieldX=rp.getClass().getField("x");//Exception in thread "main" java.lang.NoSuchFieldException: x         Field fieldX=rp.getClass().getDeclaredField("x");         fieldX.setAccessible(true);//暴力反射!解決: can not access a member of class com.itcast.day1.ReflectPoint with modifiers "private"         System.out.println(fieldX.get(rp));

 

  • 成員變量反射的綜合案例
    • 將任意一個對象中的所有String類型的成員變量所對應的字符串內容中的”b”改成”a”。
//javabeanpackage com.itcast.day1;public class ReflectFieldEx {     private String firstName;     private int age;     private String address;     public ReflectFieldEx(String firstName, int age, String address) {         super();         this.firstName = firstName;         this.age = age;         this.address = address;     }     @Override     public String toString() {         return "ReflectFieldEx [firstName=" + firstName + ", age=" + age                 + ", address=" + address + "]";     } }

 

//利用反射來替換目標字符public static void change_a2b(Object obj )throws Exception{         Field[] fields=obj.getClass().getDeclaredFields();         for(int i=0;i<fields.length;i++){             if(fields[i].getType()==String.class){//字節碼在內存中只有一份,用==比較 !                 fields[i].setAccessible(true);                 String fieldValue=fields[i].get(obj).toString();                 if(fieldValue.contains("a")){                     fieldValue=fieldValue.replace('a', 'b');//替換字符串中所有的a為b                     fields[i].set(obj,fieldValue);                 }             }         }     }
    • 測試代碼:
ReflectFieldEx rfe=new ReflectFieldEx("a1b2c3", 2, "library");  System.out.println(rfe); change_a2b(rfe); System.out.println(rfe);
    • 測試結果:
ReflectFieldEx [firstName=a1b2c3, age=2, address=library] ReflectFieldEx [firstName=b1b2c3, age=2, address=librbry]

 

2.3 Method類
  • Method類代表某個類中的一個成員方法
  • 得到類中的某一個方法:
    • 例子:

    Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt", int.class);

  • 調用方法:
    • 通常方式:System.out.println(s.charAt(1));
    • 反射方式:System.out.println(methodCharAt.invoke(s, 1));
      • 如果傳遞給Method對象的invoke()方法的一個參數為null,這有什么樣的意義呢?說明該Mehtod對象對應的是一個靜態方法!
  • jdk1.4和jdk1.5的invoke方法的區別:
    • jdk1.5, public Object invoke(Object obj,Object … args)
    • jdk1.4, public Object invoke(Object obj,Object[] args),即按jdk1.4的語法,需要將一個數組作為參數傳遞給invoke方法時,數組中的每個元素對應被調用方法的中各個參數。

 

  • 更深入的了解Method的調用-----------對接收數組參數的成員方法進行反射

                           --------用反射方法執行某個類中的main方法

    • 目標:
      • 寫一個程序,這個程序能夠根據用戶提供的類名,去執行該類的main方法。
    • 問題:
      • 啟動Java程序的main方法的參數是一個字符串數組,即public static void main(String[] args),通過反射方式來調用這個main方法時,如何為invoke方法傳遞參數呢?按照jdk1.5的語法,整個數組是一個參數,按照jdk1.4的語法,數組中的每個元素都對應一個參數,當把一個字符串作為參數傳遞給invoke方法時,javac會到底按照那種語法進行處理呢?jdk1.5肯定要兼容jdk1.4的語法,會按照jdk1.4的語法進行處理,即把數組打散成為若干個單獨的參數。所以,在給main方法傳遞參數時,不能使用代碼methodMain.invoke(null, new String[]{"xxxx"}),javac只把它當做jdk1.4的語法進行理解,而不把它當作jdk1.5的語法解釋,因此會出現參數類型不對的問題。
    • 解決辦法:
      • methodMain.invoke(null, new Object[]{new String[]{"xxxx"}})
      • methodMain.invoke(null, (Object)new String[]{"xxxx"}),編譯時不把參數當作數組看待,也就不會把數組打散成若干個參數了。
    • 測試源代碼:
      /***********成員方法的反射***********/ Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt", int.class); System.out.println(s.charAt(1));//普通方式調用charAt方法 System.out.println(methodCharAt.invoke(s, 1));//反射方式,使用對象s,并傳遞參數1,調用methodCharAt代表字節碼的方法。 System.out.println(methodCharAt.invoke(s, new Object[] { 2 }));//按照jdk1.4語法調用(還沒有可變參數)  //main方法反射調用--右鍵—run as—configuration—arguements填寫將要測試main方法所在的類名  Method methodMain=Class.forName(args[0]).getMethod("main", String[].class); //methodMain.invoke(null,new String[]{"xxxx","yyy","zzz"});//01. 會按照jdk1.4的語法進行處理,即把數組打散成為若干個單獨的參數 methodMain.invoke(null,new Object[]{new String[]{"xxxx","yyy","zzz"}});//02. 依然按照jdk1.4語法,參數打散后發現,里面只有一個元素--Stirng[] methodMain.invoke(null, (Object)new String[]{"xxxx","yyy","zzz"});//03.按照jdk1.5語法,參數不會被打散
2.4 數組的反射
    • 有相同維度和元素類型的數組屬于同一類型,即具有相同的Class實例對象。
    • 代表數組的Class實例對象的getSuperClass()方法返回的父類為Object類對應的Class。
    • 基本類型的一維數組可以被當作Object類型使用,不能當作Object[]類型使用,非基本類型的一維數組,即可以當做Object類型使用,又可以當作Object[]類型使用。
    • Arrays.asList()方法處理int[]和String[]時的差異。
    • Arrays工具類用于完成對數組的反射操作。
    • 測試代碼:
        //數組的反射        int [] a1=new int[]{1,2,3};//一維基本類型數組        int [] a2=new int[4];        int [][] a3=new int[2][3];//二維基本類型數組 = 一維數組中的元素為一維數組  = {int[3],int[3]} = 一維引用類型數組        String[] s1=new String[]{"a","b","c"};//一維引用類型數組        System.out.println(a1.getClass()==a2.getClass());                //打印數組--我們最想看到的是 每個元素被打印出來        System.out.println(a1);        System.out.println(s1);                //基本類型的一維數組可以被當作Object類型使用,不能當作Object[]類型使用,        //非基本類型的一維數組,即可以當做Object類型使用,又可以當作Object[]類型使用。        //Arrays.asList()方法處理int[]和String[]時的差異        System.out.println(Arrays.asList(a1));//[[I@7b11a3ac]  int[3]-->Object        System.out.println(Arrays.asList(s1));//[a, b, c]      String[3]-->Object[]{"a","b","c"}-->List        //[[I@35d9dc39, [I@72093dcd]  int[2][3]-->int[]{int[3],int[3]}-->Object[]{int[3],int[3]}-->Object[]{Object,Object}        System.out.println(Arrays.asList(a3));

 

    • 利用數組的反射打印 代碼:
        //利用數組的反射打印private static void printObject(Object obj) {     Class clazz=obj.getClass();     if(clazz.isArray()){         int len=Array.getLength(obj);         for(int i=0;i<len;i++){             System.out.println(Array.get(obj, i));         }     }else{         System.out.println(obj);     } }
//測試代碼
int [] a1=new int[]{1,2,3};String[] s1=new String[]{"a","b","c"};printObject(a1);//打印int[] printObject(s1);//打印String[] printObject("xxx");//打印字符串運行結果:1 2 3 a b c
      

 思考題:怎么得到數組中的元素類型?

               沒有辦法,因為Object[]里可以放各種東西,我們只能得到某一個元素的類型,而不能得到Object[]的類型

           Object[] a=new Object(){1,flase,”abc”};

            a[0].getClass().getTYPE();//得到某一元素的類型


上一篇:Java IO流

下一篇:Java線程的5個使用技巧

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 潞西市| 民县| 荔浦县| 海南省| 民丰县| 海南省| 淮安市| 漯河市| 崇文区| 同心县| 赤水市| 遂溪县| 怀仁县| 滦南县| 黄浦区| 贵州省| 大关县| 新疆| 乐都县| 丰城市| 许昌市| 福建省| 安远县| 驻马店市| 禹城市| 崇仁县| 山阴县| 芒康县| 东山县| 斗六市| 新郑市| 额敏县| 宁阳县| 新营市| 焉耆| 资兴市| 宜州市| 公安县| 方正县| 平乡县| 德庆县|