一、類加載器 二、反射構造方法 三、反射成員變量 四、反射成員方法 五、反射配置文件運行類中的方法

01類加載器

* A.類的加載
    當程序要使用某個類時,若是該類還未被加載到內存中,則系統會經過加載,鏈接,初始化三步來實現對這個類進行初始化。
    * a 加載 
        * 就是指將class文件讀入內存,併爲之建立一個Class對象。
        * 任何類被使用時系統都會創建一個Class對象
    * b 鏈接
        * 驗證 是否有正確的內部結構,並和其餘類協調一致
        * 準備 負責爲類的靜態成員分配內存,並設置默認初始化值
        * 解析 將類的二進制數據中的符號引用替換爲直接引用
    * c 初始化 
        * 就是咱們之前講過的初始化步驟(new 對象)
    * 注:簡單的說就是:把.class文件加載到內存裏,並把這個.class文件封裝成一個Class類型的對象。
* B.類的加載時機
    如下的狀況,會加載這個類。
    * a. 建立類的實例
    * b. 類的靜態變量,或者爲靜態變量賦值
    * c. 類的靜態方法
    * d. 使用反射方式來強制建立某個類或接口對應的java.lang.Class對象
    * e. 初始化某個類的子類
    * f. 直接使用java.exe命令來運行某個主類
        
* C: 類加載器(瞭解)
    負責將.class文件加載到內在中,併爲之生成對應的Class對象。
    * a. Bootstrap ClassLoader 根類加載器
        * 也被稱爲引導類加載器,負責Java核心類的加載
        * 好比System,String等。在JDK中JRE的lib目錄下rt.jar文件中

 * b. Extension ClassLoader 擴展類加載器java

* 負責JRE的擴展目錄中jar包的加載。
        * 在JDK中JRE的lib目錄下ext目錄

 * c. System ClassLoader 系統類加載器數組

* 負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑。
        * 咱們用的是System ClassLoader 系統類加載器

02反射

* A. 反射定義
    * a. JAVA反射機制是在運行狀態中,
            對於任意一個類,都可以知道這個類的全部屬性和方法;
            對於任意一個對象,都可以調用它的任意一個方法和屬性;
        這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。

    * b.反射技術
        條件:運行狀態
        已知:一個類或一個對象(根本是已知.class文件)
        結果:獲得這個類或對象的全部方法和屬性

    * 注: 要想解剖一個類,必須先要獲取到該類的字節碼文件對象。而解剖使用的就是Class類中的方法.因此先要獲取到每個字節碼文件對應的Class類型的對象。
* B. Class類
    * a. Class類及Class對象的瞭解
        要想解剖一個類,必須先了解Class對象。
        閱讀API的Class類得知,Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及經過調用類加載器中的 defineClass 方法自動構造的。
    * b. 獲得Class對象
        * 1. 有三個方法
            方式一: 經過Object類中的getClass()方法
                Person person = new Person();
                Class clazz = person.getClass();
            方式二: 經過 類名.class 獲取到字節碼文件對象(任意數據類型都具有一個class靜態屬性,看上去要比第一種方式簡單)。
                Class clazz = Person.class;
            方式三: 經過Class類中的方法(將類名做爲字符串傳遞給Class類中的靜態方法forName便可)。
                Class c3 = Class.forName("Person");
            注:第三種和前兩種的區別是:
                    前兩種你必須明確Person類型.
                    後面是指定這種類型的字符串就行.這種擴展更強.我不須要知道你的類.我只提供字符串,按照配置文件加載就能夠了

        * 2. 獲得Class對象的三個方法代碼演示:
            代碼演示
            /*
             * 獲取.class字節碼文件對象的方式
             *         1:經過Object類中的getObject()方法
             *         2: 經過 類名.class 獲取到字節碼文件對象
             *         3: 反射中的方法,
             *             public static Class<?> forName(String className) throws ClassNotFoundException
             *             返回與帶有給定字符串名的類或接口相關聯的 Class 對象 
             */
            public class ReflectDemo {
                public static void main(String[] args) throws ClassNotFoundException {
                    // 1: 經過Object類中的getObject()方法
                    // Person p1 = new Person();
                    // Class c1 = p1.getClass();
                    // System.out.println("c1 = "+ c1);

                    // 2: 經過 類名.class 獲取到字節碼文件對象
                    // Class c2 = Person.class;
                    // System.out.println("c2 = "+ c2);

                    // 3: 反射中的方法
                    Class c3 = Class.forName("cn.itcast_01_Reflect.Person");// 包名.類名
                    System.out.println("c3 = " + c3);
                }
            }

 Person類安全

package cn.itcast_01_Reflect;
            public class Person {
                //成員變量
                public String name;
                public int age;
                private String address;

                //構造方法
                public Person() {
                    System.out.println("空參數構造方法");
                }
                
                public Person(String name) {
                    this.name = name;
                    System.out.println("帶有String的構造方法");
                }
                //私有的構造方法
                private Person(String name, int age){
                    this.name = name;
                    this.age = age;
                    System.out.println("帶有String,int的構造方法");
                }
                
                public Person(String name, int age, String address){
                    this.name = name;
                    this.age = age;
                    this.address = address;
                    System.out.println("帶有String, int, String的構造方法");
                }
                
                //成員方法
                //沒有返回值沒有參數的方法
                public void method1(){
                    System.out.println("沒有返回值沒有參數的方法");
                }
                //沒有返回值,有參數的方法
                public void method2(String name){
                    System.out.println("沒有返回值,有參數的方法 name= "+ name);
                }
                //有返回值,沒有參數
                public int method3(){
                    System.out.println("有返回值,沒有參數的方法");
                    return 123;
                }
                //有返回值,有參數的方法
                public String method4(String name){
                    System.out.println("有返回值,有參數的方法");
                    return "哈哈" + name;
                }
                //私有方法
                private void method5(){
                    System.out.println("私有方法");
                }

                @Override
                public String toString() {
                    return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
                }
            }
        * 注: Class類型的惟一性
            由於一個.class文件在內存裏只生成一個Class對象,因此不管那一種方法獲得Class對象,獲得的都是同一個對象。
* C.經過反射獲取無參構造方法並使用
    * a. 獲得無參構造方法
        public Constructor<?>[] getConstructors() 
            獲取全部的public 修飾的構造方法。
            選擇無參構造方法,不建議使用。
        public Constructor<T> getConstructor(Class<?>... parameterTypes) 
            獲取public修飾, 指定參數類型所對應的構造方法。
            不傳參數獲得無參構造方法。
    * b. 運行無參構造方法
        public T newInstance(Object... initargs) 
            使用此 Constructor 對象表示的構造方法來建立該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。 
            由於是無參構造,因此不傳參數。
    * c. 經過反射獲取無參構造方法並使用的代碼演示:
            package cn.itcast.demo1;

            import java.lang.reflect.Constructor;

            /*
             *  經過反射獲取class文件中的構造方法,運行構造方法
             *  運行構造方法,建立對象
             *    獲取class文件對象
             *    從class文件對象中,獲取須要的成員
             *    
             *  Constructor 描述構造方法對象類
             */
            public class ReflectDemo1 {
                public static void main(String[] args) throws Exception {
                
                    Class c = Class.forName("cn.itcast.demo1.Person");
                    //使用class文件對象,獲取類中的構造方法
                    //  Constructor[]  getConstructors() 獲取class文件對象中的全部公共的構造方法
                    /*Constructor[] cons = c.getConstructors();
                    for(Constructor con : cons){
                        System.out.println(con);
                    }*/
                    //獲取指定的構造方法,空參數的構造方法
                    Constructor con =  c.getConstructor();
                    //運行空參數構造方法,Constructor類方法 newInstance()運行獲取到的構造方法
                    Object obj = con.newInstance();
                    System.out.println(obj.toString());
                }
            }
* D. 經過反射獲取有參構造方法並使用
    * a. 獲得有參的構造方法
        public Constructor<T> getConstructor(Class<?>... parameterTypes) 
            獲取public修飾, 指定參數類型所對應的構造方法。
            傳相應的參數類型獲得有參構造方法。
    * b. 運行無參構造方法
        public T newInstance(Object... initargs) 
            使用此 Constructor 對象表示的構造方法來建立該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。 
            由於是有參構造,因此傳相應的參數值。
    * c. 經過反射獲取有參構造方法並使用的代碼演示:
        package cn.itcast.demo1;

        import java.lang.reflect.Constructor;

        /*
         *  經過反射,獲取有參數的構造方法並運行
         *  方法getConstructor,傳遞能夠構造方法相對應的參數列表便可
         */
        public class ReflectDemo2 {
            public static void main(String[] args)throws Exception {
                Class c = Class.forName("cn.itcast.demo1.Person");
                //獲取帶有,String和int參數的構造方法
                //Constructor<T> getConstructor(Class<?>... parameterTypes)  
                //Class<?>... parameterTypes 傳遞要獲取的構造方法的參數列表
                Constructor con = c.getConstructor(String.class,int.class);
                //運行構造方法
                // T newInstance(Object... initargs)  
                //Object... initargs 運行構造方法後,傳遞的實際參數
                Object obj = con.newInstance("張三",20);
                System.out.println(obj);
            }
        }
* E. 經過反射獲取有參構造方法並使用快捷方式
    * a. 使用的前提
        類有空參的公共構造方法。(若是是同包,默認權限也能夠)
    * b. 使用的基礎
        Class類的 public T newInstance() 方法
             建立此 Class 對象所表示的類的一個新實例。
    * c. 經過反射獲取有參構造方法並使用快捷方式的代碼演示:
        package cn.itcast.demo1;
        /*
         * 反射獲取構造方法並運行,有快捷點的方式
         * 有前提:
         *   被反射的類,必須具備空參數構造方法
         *   構造方法權限必須public
         */
        public class ReflectDemo3 {
            public static void main(String[] args) throws Exception {
                Class c = Class.forName("cn.itcast.demo1.Person");
                // Class類中定義方法, T newInstance() 直接建立被反射類的對象實例
                Object obj = c.newInstance();
                System.out.println(obj);
            }
        }
* F. 經過反射獲取私有構造方法並使用
    * a. 獲得私有的構造方法
        public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
            獲取指定參數類型所對應的構造方法(包含私有的)。
        public Constructor<?>[] getDeclaredConstructors() 
            獲取全部的構造方法(包含私有的)。
    * b. 運行私有構造方法
        public void setAccessible(boolean flag)
            將此對象的 accessible 標誌設置爲指示的布爾值。
            設置爲true,這個方法保證咱們獲得的私有構造方法的運行。(取消運行時期的權限檢查。)
        public T newInstance(Object... initargs) 
            使用此 Constructor 對象表示的構造方法來建立該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。 
    * c. 經過反射獲取私有構造方法並使用的代碼演示:
        package cn.itcast.demo1;

        import java.lang.reflect.Constructor;

        /*
         *  反射獲取私有的構造方法運行
         *  不推薦,破壞了程序的封裝性,安全性
         *  暴力反射
         */
        public class ReflectDemo4 {
            public static void main(String[] args) throws Exception{
                Class c = Class.forName("cn.itcast.demo1.Person");
                //Constructor[] getDeclaredConstructors()獲取全部的構造方法,包括私有的
                /*Constructor[] cons = c.getDeclaredConstructors();
                for(Constructor con : cons){
                    System.out.println(con);
                }*/
                //Constructor getDeclaredConstructor(Class...c)獲取到指定參數列表的構造方法
                Constructor con = c.getDeclaredConstructor(int.class,String.class);
                
                //Constructor類,父類AccessibleObject,定義方法setAccessible(boolean b)
                con.setAccessible(true);
                
                Object obj = con.newInstance(18,"lisi");
                System.out.println(obj);
            }
        }
    * 注:不推薦,破壞了程序的封裝性,安全性。
* G. 反射獲取成員變量並改值
    * a. 獲取成員變量
        * 獲得公共的成員變量
            public Field getField(String name) 
                返回一個 Field 對象,它反映此 Class 對象所表示的類或接口的指定公共成員字段。 
            public Field[] getFields() 
                返回一個包含某些 Field 對象的數組,這些對象反映此 Class 對象所表示的類或接口的全部可訪問公共字段。 
        * 獲得全部的成員變量(包括私有的,若是要進行修改私有成員變量,要先進行public void setAccessible(boolean flag) 設置。)
            public Field getDeclaredField(String name) 
                返回一個 Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段。 
            public Field[] getDeclaredFields() 
                返回 Field 對象的一個數組,這些對象反映此 Class 對象所表示的類或接口所聲明的全部字段。 
    * b. 修改爲員變量(Field)的值
        * 修改公共的成員變量
            public void set(Object obj, Object value) 
                將指定對象變量上此 Field 對象表示的字段設置爲指定的新值。 
                obj指的是修改的是那個對象的這個成員變量值。
    * c. 反射獲取成員變量並改值的代碼演示
        package cn.itcast.demo1;
        import java.lang.reflect.Field;
        /*
         *  反射獲取成員變量,並修改值
         *  Person類中的成員String name
         */
        public class ReflectDemo5 {
            public static void main(String[] args) throws Exception{
                Class c = Class.forName("cn.itcast.demo1.Person");
                Object obj = c.newInstance();
                //獲取成員變量 Class類的方法 getFields() class文件中的全部公共的成員變量
                //返回值是Field[]    Field類描述成員變量對象的類
                /*Field[] fields = c.getFields();
                for(Field f : fields){
                    System.out.println(f);
                }*/
                
                //獲取指定的成員變量 String name
                //Class類的方法  Field getField(傳遞字符串類型的變量名) 獲取指定的成員變量
                Field field = c.getField("name");
               
                //Field類的方法 void set(Object obj, Object value) ,修改爲員變量的值
                //Object obj 必須有對象的支持,  Object value 修改後的值
                field.set(obj,"王五");
                System.out.println(obj);
                
            }
        }
* H. 反射獲取空參數成員方法並運行
    * a. 獲取空參數成員方法
        * 獲得公共的成員方法
            public Method getMethod(String name, Class<?>... parameterTypes) 
                返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。 
            public Method[] getMethods()
                返回一個包含某些 Method 對象的數組,這些對象反映此 Class對象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共 member 方法。
        * 獲得所有的成員方法(包括私有的,若是要使用私有成員方法,要先進行public void setAccessible(boolean flag) 設置。)
            public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
                返回一個 Method 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法。 
            public Method[] getDeclaredMethods() 
                返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或接口聲明的全部方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。 
    * b. 使用Method方法對象
        public Object invoke(Object obj, Object... args) 
            對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。 
            obj 指的是調這個方法的對象。
            args 指的是調用這個方法所要用到的參數列表。
            返回值Object就是方法的返回對象。若是方法沒有返回值 ,返回的是null.
    * c. 反射獲取空參數成員方法並運行代碼演示
        package cn.itcast.demo1;

        import java.lang.reflect.Method;

        /*
         *  反射獲取成員方法並運行
         *  public void eat(){}
         */
        public class ReflectDemo6 {
            public static void main(String[] args) throws Exception{
                Class c = Class.forName("cn.itcast.demo1.Person");
                Object obj = c.newInstance();
                //獲取class對象中的成員方法
                // Method[] getMethods()獲取的是class文件中的全部公共成員方法,包括繼承的
                // Method類是描述成員方法的對象
                /*Method[] methods = c.getMethods();
                for(Method m : methods){
                    System.out.println(m);
                }*/
                
                //獲取指定的方法eat運行
                // Method getMethod(String methodName,Class...c)
                // methodName獲取的方法名  c 方法的參數列表
                Method method = c.getMethod("eat");
                //使用Method類中的方法,運行獲取到的方法eat
                //Object invoke(Object obj, Object...o)
                method.invoke(obj);
            }
        }

* I. 反射獲取有參數成員方法並運行
    * a. 獲取有參數成員方法
        * 獲得公共的成員方法
            public Method getMethod(String name, Class<?>... parameterTypes) 
                返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。 
            public Method[] getMethods()
                返回一個包含某些 Method 對象的數組,這些對象反映此 Class對象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共 member 方法。
        * 獲得所有的成員方法(包括私有的,若是要使用私有成員方法,要先進行public void setAccessible(boolean flag) 設置。)
            public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
                返回一個 Method 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法。 
            public Method[] getDeclaredMethods() 
                返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或接口聲明的全部方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。 
    * b. 使用Method方法對象
        public Object invoke(Object obj, Object... args) 
            對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。 
            obj 指的是調這個方法的對象。
            args 指的是調用這個方法所要用到的參數列表。
            返回值Object就是方法的返回對象。若是方法沒有返回值 ,返回的是null.
    * c. 反射獲取有參數成員方法並運行代碼演示
        package cn.itcast.demo1;
        import java.lang.reflect.Method;

        /*
         *  反射獲取有參數的成員方法並執行
         *  public void sleep(String,int,double){}
         */
        public class ReflectDemo7 {
            public static void main(String[] args) throws Exception{
                Class c = Class.forName("cn.itcast.demo1.Person");
                Object obj = c.newInstance();
                //調用Class類的方法getMethod獲取指定的方法sleep
                Method method = c.getMethod("sleep", String.class,int.class,double.class);
                //調用Method類的方法invoke運行sleep方法
                method.invoke(obj, "休眠",100,888.99);
            }
        }
* J. 反射泛型擦除
    * a. 使用狀況
        例如:在泛型爲String的集合裏,添加Integer的數據
        ArrayList<String> list = new ArrayList<String>();
        list.add(100);
    * b. 能用泛型擦除的理論
        僞泛型:在編譯後的.class文件裏面是沒有泛型的。類型爲Object。
        用反射的方法繞過編譯,獲得Class文件對象,直接調用add方法。
    * c. 反射泛型擦除的代碼演示
        package cn.itcast.demo2;
        import java.lang.reflect.Method;
        import java.util.ArrayList;

        /*
         *   定義集合類,泛型String
         *   要求向集合中添加Integer類型
         *   
         *   反射方式,獲取出集合ArrayList類的class文件對象
         *   經過class文件對象,調用add方法
         *   
         *   對反射調用方法是否理解
         */
        public class ReflectTest {
            public static void main(String[] args)throws Exception {
                ArrayList<String> array  = new ArrayList<String>();
                array.add("a");
                //反射方式,獲取出集合ArrayList類的class文件對象
                Class c = array.getClass();
                //獲取ArrayList.class文件中的方法add
                Method method = c.getMethod("add",Object.class);
                //使用invoke運行ArrayList方法add
                method.invoke(array, 150);
                method.invoke(array, 1500);
                method.invoke(array, 15000);
                System.out.println(array);
                
                
            }
        }
* K. 反射經過配置文件來決定運行的步驟
    * a. 操做依據
            經過配置文件獲得類名和要運行的方法名,用反射的操做類名獲得對象和調用方法
    * b. 實現步驟:
         *    1. 準備配置文件,鍵值對
         *    2. IO流讀取配置文件  Reader
         *    3. 文件中的鍵值對存儲到集合中 Properties
         *        集合保存的鍵值對,就是類名和方法名
         *    4. 反射獲取指定類的class文件對象
         *    5. class文件對象,獲取指定的方法
         *    6. 運行方法
    * c. 代碼演示
        代碼:
        package cn.itcast.demo3;

        import java.io.FileReader;
        import java.lang.reflect.Method;
        import java.util.Properties;

        /*
         *  調用Person方法,調用Student方法,調用Worker方法
         *  類不清楚,方法也不清楚
         *  經過配置文件實現此功能
         *    運行的類名和方法名字,以鍵值對的形式,寫在文本中
         *    運行哪一個類,讀取配置文件便可
         *  實現步驟:
         *    1. 準備配置文件,鍵值對
         *    2. IO流讀取配置文件  Reader
         *    3. 文件中的鍵值對存儲到集合中 Properties
         *        集合保存的鍵值對,就是類名和方法名
         *    4. 反射獲取指定類的class文件對象
         *    5. class文件對象,獲取指定的方法
         *    6. 運行方法
         */
        public class Test {
            public static void main(String[] args) throws Exception{
                //IO流讀取配置文件
                FileReader r = new FileReader("config.properties");
                //建立集合對象
                Properties pro = new Properties();
                //調用集合方法load,傳遞流對象
                pro.load(r);
                r.close();
                //經過鍵獲取值
                String className = pro.getProperty("className");
                String methodName = pro.getProperty("methodName");
                //反射獲取指定類的class文件對象
                Class c = Class.forName(className);
                Object obj = c.newInstance();
                //獲取指定的方法名
                Method method = c.getMethod(methodName);
                method.invoke(obj);
            }
        }
        配置文件:
        #className=cn.itcast.demo3.Student
        #methodName=study
        className=cn.itcast.demo3.Person
        methodName=eat
        #className=cn.itcast.demo3.Worker
        #methodName=job

3總結

  • 把今天的知識點總結一遍。ide

相關文章
相關標籤/搜索