1. 程式人生 > >33_反射(類加載、反射)_講義

33_反射(類加載、反射)_講義

類加載 ext 我不 alt name 擴展類 trap 生成 字節

今日內容介紹
1、類加載器
2、反射構造方法
3、反射成員變量
4、反射成員方法
5、反射配置文件運行類中的方法

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 擴展類加載器
          * 負責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

    作業測試

1.ArrayList list = new ArrayList();

這個泛型為Integer的ArrayList中存放一個String類型的對象

2.用反射去創建一個對象,有2種方式,盡量用代碼去體現

  1. 編寫一個類,增加一個實例方法用於打印一條字符串。
  2. 並使用反射手段創建該類的對象, 並調用該對象中的方法。

4.編寫一個類A,增加一個實例方法showString,用於打印一條字符串,

在編寫一個類TestA ,作為客戶端,用鍵盤輸入一個字符串,該字符串就是類A的全名,使用反射機制創建該類的對象,
並調用該對象中的方法showString
  1. 寫一個方法,此方法可將obj對象中名為propertyName的屬性的值設置為value.

     public void setProperty(Object obj, String propertyName, Object value){   
     }

6.定義一個標準的JavaBean,名叫Person,包含屬性name、age。

使用反射的方式創建一個實例、調用構造函數初始化name、age,使用反射方式調用setName方法對名稱進行設置,
不使用setAge方法直接使用反射方式對age賦值。

7.已知一個類,定義如下:

package com.itheima; 
public class DemoClass { 
    public void run() { 
        System.out.println("welcome to heima!"); 
    } 
} 
(1)寫一個Properties格式的配置文件,配置類的完整名稱。
(2) 寫一個程序,讀取這個Properties配置文件,獲得類的完整名稱並加載這個類,
(3)用反射 的方式運行run方法。
  1. 寫一個方法,此方法可以獲取obj對象中名為propertyName的屬性的值

     public Object getProperty(Object obj, String propertyName, Object value){ 
    
     }

答案:

1.ArrayList<Integer> list = new ArrayList<Integer>(); 
    這個泛型為Integer的ArrayList中存放一個String類型的對象
    
    package com.itheima.tests;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;

    /*
     * 1.ArrayList<Integer> list = new ArrayList<Integer>(); 
        為這個泛型為Integer的ArrayList中存放一個String類型的對象
     */
    public class Test01 {
        public static void main(String[] args) throws Exception {
            ArrayList<Integer> list = new ArrayList<Integer>(); 
            list.add(10);
            System.out.println(list);
            //通過反射獲取ArrayList集合的字節碼對象
            Class clazz = Class.forName("java.util.ArrayList");
            //通過反射獲取add方法
            Method addMethod = clazz.getMethod("add", Object.class);
            //通過反射調用addMethod方法
            addMethod.invoke(list, "reflect is very good!");
            System.out.println(list);
    //      HashMap<Integer,Integer> hm = new HashMap<>();
        }
    }

2.用反射去創建一個對象,有2種方式,盡量用代碼去體現

package com.itheima.tests;
import java.lang.reflect.Constructor;
/**
 * 2.用反射去創建一個對象,有2種方式,盡量用代碼去體現
 * @author JX
 *
 */
public class Test02 {
    public static void main(String[] args) throws Exception {
        //獲取Student類的字節碼對象
        Class clazz = Class.forName("com.itheima.tests.Student");
        //1.利用反射創建一個空的對象
        Student student = (Student)clazz.newInstance();
        /*//2.獲取字段
        Field ageField = clazz.getDeclaredField("age");
        Field nameField = clazz.getDeclaredField("name");
        //取出私有屬性
        ageField.setAccessible(true);
        nameField.setAccessible(true);
        //3.給字段設置值
        ageField.set(student, 30);
        nameField.set(student, "張三");
        System.out.println(student);*/
        
        /*Method setAgeMethod = clazz.getMethod("setAge", int.class);
        Method setNameMethod = clazz.getMethod("setName", String.class);
        setAgeMethod.invoke(student, 38);
        setNameMethod.invoke(student, "柳巖");
        System.out.println(student);*/
        //獲取有參構造
        Constructor constructor = clazz.getConstructor(int.class,String.class);
        
        Student stu = (Student)constructor.newInstance(30,"張亮");
        System.out.println(stu);
    }
}
class Student {
    private int age;
    private String name;
    public Student() {
    }
    public Student(int age, String name) {
        super();
        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 + "]";
    }   
}
  1. 編寫一個類,增加一個實例方法用於打印一條字符串。
    並使用反射手段創建該類的對象, 並調用該對象中的方法。

     package com.itheima.tests;
     import java.lang.reflect.Method;
    
     /*
      * 3. 編寫一個類,增加一個實例方法用於打印一條字符串。
         並使用反射手段創建該類的對象, 並調用該對象中的方法。
      */
     public class Test03 {
         public static void main(String[] args) throws Exception {
             //獲取Demo.java的字節碼對象
             Class clazz = Class.forName("com.itheima.tests.Demo");//obj.getClass(); Student.class
             //利用反射創建對象
             Demo demo = (Demo)clazz.newInstance();
             //利用反射獲取print方法
             Method printMethod = clazz.getMethod("print",String.class);
             printMethod.invoke(demo,"Android");
         }
     }
     class Demo {
         public Demo() {
         }
         public void print(String str ){
             System.out.println("Hello "+str);
         }

    }

4.編寫一個類A,增加一個實例方法showString,用於打印一條字符串,
在編寫一個類TestA ,作為客戶端,用鍵盤輸入一個字符串,該字符串就是類A的全名,使用反射機制創建該類的對象,並調用該對象中的方法showString

package com.itheima.tests;
import java.lang.reflect.Method;
import java.util.Scanner;

/*
 * 4.編寫一個類A,增加一個實例方法showString,用於打印一條字符串,
    在編寫一個類TestA ,作為客戶端,用鍵盤輸入一個字符串,該字符串就是類A的全名,使用反射機制創建該類的對象,
    並調用該對象中的方法showString
 */
public class Test04 {
    public static void main(String[] args) throws Exception{
        Scanner sc = new Scanner(System.in);
        System.out.println("請輸入一個類的全類名,用.隔開:");
        String className = sc.nextLine();
        
        Class clazz = Class.forName(className);//obj.getClass(); Student.class
        //利用反射創建對象
        A a = (A)clazz.newInstance();
        //利用反射獲取print方法
        Method printMethod = clazz.getMethod("showString");
        printMethod.invoke(a);      
    }
}
class A {
    public void showString() {
        System.out.println(" e m t f 明天一定通過!");
    }
}
  1. 寫一個方法,此方法可將obj對象中名為propertyName的屬性的值設置為value.

     public void setProperty(Object obj, String propertyName, Object value){   
     }
    
     package com.itheima.tests;
     import java.lang.reflect.Field;
    
     /**
      * 5. 寫一個方法,此方法可將obj對象中名為propertyName的屬性的值設置為value.   
         public void setProperty(Object obj, String propertyName, Object value){   
    
         }
      * @author JX
      *
      */
     public class Test05 {
         public static void main(String[] args) throws Exception {
             Student student = new Student(30,"張三");
             setProperty(student, "age", 25);
             System.out.println(student);
    
             Object obj = getProperty(student, "name");
             System.out.println(obj);
         }
         //給對象obj的名字為propertyName的屬性設置為value
         public static void setProperty(Object obj, String propertyName, Object value) throws Exception{   
             //1.獲取obj的字節碼對象
             Class clazz = obj.getClass();
             //2.獲取propertyName屬性對應的Field對象
             Field propertyNameField = clazz.getDeclaredField(propertyName);
             //3.設置成可訪問的
             propertyNameField.setAccessible(true);
             //4.調用set方法給對象賦值
             propertyNameField.set(obj, value);
    
         }
         //給對象obj的名字為propertyName的屬性設置為value
         public static Object getProperty(Object obj, String propertyName) throws Exception{   
             //1.獲取obj的字節碼對象
             Class clazz = obj.getClass();
             //2.獲取propertyName屬性對應的Field對象
             Field propertyNameField = clazz.getDeclaredField(propertyName);
             //3.設置成可訪問的
             propertyNameField.setAccessible(true);
             //4.調用get方法獲取該對象對應屬性的值
             return propertyNameField.get(obj);
         }
     }

6.定義一個標準的JavaBean,名叫Person,包含屬性name、age。

    使用反射的方式創建一個實例、調用構造函數初始化name、age,使用反射方式調用setName方法對名稱進行設置,
    不使用setAge方法直接使用反射方式對age賦值。
    
    package com.itheima.tests;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;

    /*
     * 6.定義一個標準的JavaBean,名叫Person,包含屬性name、age。
        使用反射的方式創建一個實例、調用構造函數初始化name、age,使用反射方式調用setName方法對名稱進行設置,
        不使用setAge方法直接使用反射方式對age賦值。
     */
    public class Test06 {
        public static void main(String[] args) throws Exception, SecurityException {
            //1.獲取Person類的字節碼對象
            Class clazz = Person.class;
            //2.利用反射獲取有參構造方法
            Constructor constructor  = clazz.getConstructor(int.class,String.class);
            //3.調用構造方法,給屬性初始化
            Person person =  (Person)constructor.newInstance(30,"滅絕師太");
            System.out.println(person);
            //4.使用反射方式調用setName方法對名稱進行設置
            Method setNameMethod = clazz.getMethod("setName", String.class);
            setNameMethod.invoke(person, "張三豐");
            //5.不使用setAge方法直接使用反射方式對age賦值。
            Field ageField = clazz.getDeclaredField("age");
            ageField.setAccessible(true);
            ageField.set(person, 50);
            System.out.println(person);
        }
    }
    class Person {
        private int age;
        private String name;
        public Person() {
        }
        public Person(int age, String name) {
            super();
            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 "Person [age=" + age + ", name=" + name + "]";
        }   
    }

7.已知一個類,定義如下:

    package com.itheima; 
    public class DemoClass { 
        public void run() { 
            System.out.println("welcome to heima!"); 
        } 
    } 
    (1)寫一個Properties格式的配置文件,配置類的完整名稱。
    (2) 寫一個程序,讀取這個Properties配置文件,獲得類的完整名稱並加載這個類,
    (3)用反射 的方式運行run方法。
    
    package com.itheima.tests;
    import java.io.BufferedReader;
    import java.io.FileInputStream;
    import java.io.FileReader;
    import java.lang.reflect.Method;
    import java.util.Properties;

    /**
     * 7.已知一個類,定義如下: 
        package com.itheima.tests; 
        public class DemoClass { 
            public void run() { 
                System.out.println("welcome to heima!"); 
            } 
        } 
        (1)寫一個Properties格式的配置文件,配置類的完整名稱。
        (2) 寫一個程序,讀取這個Properties配置文件,獲得類的完整名稱並加載這個類,
        (3)用反射 的方式運行run方法。
     * @author JX
     *
     */
    public class Test07 {
        public static void main(String[] args) throws Exception {
            /*Properties props = new Properties();
            props.load(new FileInputStream("src/config.properties"));
            String className = (String) props.get("className");*/
            //定義字符緩沖輸入流
            BufferedReader br = new BufferedReader(new FileReader("src/config.properties"));
            String line = br.readLine();
            String className = line.split("=")[1];
    //      System.out.println(className);
            Class clazz = Class.forName(className);
            //利用反射創建一個對象
            Object obj = clazz.newInstance();
            //利用反射獲取run方法
            Method runMethod = clazz.getMethod("run");
            //利用反射調用run方法
            runMethod.invoke(obj);      
        }
    }
  1. 寫一個方法,此方法可以獲取obj對象中名為propertyName的屬性的值

     public static Object getProperty(Object obj, String propertyName, Object value){ 
    
     }
    
     package com.itheima.tests2;
     import java.lang.reflect.Field;
     public class Test08 {
         public static void main(String[] args) throws Exception {
             Person person = new Person(20,"張三");
             String name = (String)getProperty(person, "name");
             System.out.println(name);
    
         }
         public static Object getProperty(Object obj, String propertyName) throws Exception{ 
             //獲取obj對象的字節碼文件對象
             Class clazz = obj.getClass();
             //獲取propertyName屬性所對應的字段
             Field field = clazz.getDeclaredField(propertyName);
             //去掉私有屬性
             field.setAccessible(true);
             return field.get(obj);
         }
    
     }

33_反射(類加載、反射)_講義