胖哥說反射 上卷

我來學習反射

1.爲何咱們要學習反射?

經過反射機制能夠獲取到一個類的完整信息,例如:全部(包含private修飾)屬性和方法,包信息等。html

換句話說,Class自己表示一個類的自己,經過Class能夠完整獲取一個類中的完整結構,包含此類中的方法定義,屬性定義等。java

反射就是把Java類中的各類成分映射成一個個的Java對象api

例如:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術能夠對一個類進行解剖,把個個組成部分映射成一個個對象。

2.反射的核心是什麼?

我我的認爲:一切的操做都是講使用Object完成,類或者數組的引用是能夠用Object進行接收。數組

也就是咱們以前說Java中的我認爲很重要的多態,對象的多態:Object object= 任何引用類型的實例對象安全

3.類的加載過程

類的正常加載過程:反射的原理在與Class對象oracle

Class對象的由來是將class文件讀入內存,併爲之建立一個Class對象,那麼Class對象在反射中起到什麼做用?ide

咱們用圖片已經說明了很清楚了,咱們在來看一下官方的解釋函數

For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.Class which provides methods to examine the runtime properties of the object including its members and type information. Class also provides the ability to create new classes and objects. Most importantly, it is the entry point for all of the Reflection APIs.

對於每一種類,Java虛擬機都會初始化出一個Class類型的實例,每當咱們編寫而且編譯一個新建立的類就會產生一個對應Class對象,而且這個Class對象會被保存在同名.class文件裏。當咱們new一個新對象或者引用靜態成員變量時,Java虛擬機(JVM)中的類加載器系統會將對應Class對象加載到JVM中,而後JVM再根據這個類型信息相關的Class對象建立咱們須要實例對象或者提供靜態變量的引用值。性能

如上圖所示,好比建立編譯一個Student類,那麼,JVM就會建立一個Student對應Class類的Class實例,該Class實例保存了Student類相關的類型信息,包括屬性,方法,構造方法等等,經過這個Class實例能夠在運行時訪問Student對象的屬性和方法等。另外經過Class類還能夠建立出一個新的Student對象。這就是反射可以實現的緣由,能夠說Class是反射操做的基礎。學習

須要特別注意的是,每一個class(注意class是小寫,表明普通類)類,不管建立多少個實例對象,在JVM中都對應同一個Class對象。

4.Class API簡要說明

跟咱們以前學習查看Math、String類同樣的過程

Class 類的實例表示正在運行的 Java 應用程序中的類和接口。也就是JVM中有N多的實例每一個類都有該Class對象。(包括基本數據類型)

Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及經過調用類加載器中的defineClass 方法自動構造的。也就是這不須要咱們本身去處理建立,JVM已經幫咱們建立好了。

沒有公共的構造方法,方法共有64個太多了。下面用到哪一個就詳解哪一個吧

5.反射的使用

Java 提供反射機制,依賴於 Class 類和 java.lang.reflect 類庫。其主要的類以下:

  1. Class:表示類或者接口
  2. Field:表示類中的成員變量
  3. Method:表示類中的方法
  4. Constructor:表示類的構造方法
  5. Array:該類提供了動態建立數組和訪問數組元素的靜態方法

先本身設置一個Student類來完成對應的測試,代碼以下:

package com.pangsir.model;

public class Student {
    public int no;
    public String sex;

    private String name;
    private int age;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    /*
     * 構造方法
     */
    
   Student(String str){  
       System.out.println("(默認)的構造方法 s = " + str);  
   }  
     
   //無參構造方法  
   public Student(){  
       System.out.println("調用了公有、無參構造方法執行了。。。");  
   }  
     
   //有一個參數的構造方法  
   protected Student(char name){  
       System.out.println("姓名:" + name);  
   }  
     
   //有多個參數的構造方法  
   public Student(String name ,int age){  
       System.out.println("姓名:"+name+"年齡:"+ age);//這的執行效率有問題,之後解決。  
   }  
     
   //私有構造方法  
   private Student(int age){  
       System.out.println("私有的構造方法   年齡:"+ age);  
   }
    
}

5.1 獲取Class對象的三種方式

說Class是反射可以實現的基礎的另外一個緣由是:Java反射包java.lang.reflect中的全部類都沒有public構造方法,要想得到這些類實例,只能經過Class類獲取。因此說若是想使用反射,必須得得到Class對象。

/*
 * Constructor. Only the Java Virtual Machine creates Class
 * objects.
 */
private Class() {}
  • Object.getClass() 方法(對象.getClass())

    若是咱們有一個類的對象,那麼咱們能夠經過 Object.getClass 方法得到該類的 Class 對象。

    // String 對象的 getClass 方法
    Class clazz1 = "hello".getClass();
    // 數組對象的 getClass 方法
    Class clazz2 = (new byte[1024]).getClass();
    System.out.println(class2) // 會輸出 [B, [ 表明是數組, B 表明是 byte。即 byte 數組的類類型

    然而對於基本類型沒法使用這種方法:

    boolean b;
    Class c = b.getClass();   // compile-time error
  • class 語法

    任何數據類型(包括基本數據類型)都有一個「靜態」的class屬性,若咱們知道要獲取的類類型的名稱時,咱們可使用 class 語法獲取該類類型的對象

    // 類
    Class clazz = Integer.class;
    // 數組
    Class clazz2 = int [][].class;
    • 包裝類的 TYPE 靜態屬性

      對於基本類型和 void 都有對應的包裝類。在包裝類中有一個靜態屬性 TYPE,保存了該來的類類型。以 Integer 類爲例,其源碼中定義了以下的靜態屬性:

      @SuppressWarnings("unchecked")
      public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

      生成 Class 類實例的方法:

      Class clazz1 = Integer.TYPE;
      Class clazz2 = Void.TYPE;
  • Class.forName() 方法

    經過Class類的靜態方法:forName(String className)(經常使用)

    能夠經過 Class 的 forName 方法獲取 Class 實例,其中類的名稱要寫類的完整路徑。

    該方法只能用於獲取引用類型的類類型對象。

    // 這種方式會使用當前的類的加載器加載,而且會將 Class 類實例初始化
    Class<?> clazz = Class.forName("java.lang.String");
    // 上面的調用方式等價於
    Class<?> clazz = Class.forName("java.lang.String", true, currentLoader);

    對於數組比較特殊:

    Class cDoubleArray = Class.forName("[D");    //至關於double[].class
    
    Class cStringArray = Class.forName("[[Ljava.lang.String;");   //至關於String[][].class

    使用該方法可能會拋出 ClassNotFoundException 異常,這個異常發生在類的加載階段,緣由以下:

    • 類加載器在類路徑中沒有找到該類(檢查:查看所在加載的類以及其所依賴的包是否在類路徑下)
    • 該類已經被某個類加載器加載到 JVM 內存中,另一個類加載器又嘗試從同一個包中加載

5.2 Student類獲取Class

package com.pangsir;

import java.lang.reflect.Constructor;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Student student = new Student();
        
        /*
         * JAVA反射--獲取Class對象的三種方式
         */
        
        // 經過對象名.getClass()方法獲取
        Class stuClass = student.getClass();
        System.out.println("stuClass is "+stuClass.getName());        
        
        // 經過類名.class方式得到
        Class stuClass1 = Student.class;
        System.out.println("stuClass1 is "+stuClass1.getName());
        System.out.println(stuClass == stuClass1);
        
        // 經過Class.forName()方法得到
        Class stuClass2 = Class.forName("com.pangsir.model.Student");
        System.out.println("stuClass2 is "+stuClass2);
        System.out.println(stuClass1 == stuClass2);
    }
}

Output:
stuClass is com.pangsir.model.Student
stuClass1 is com.pangsir.model.Student
true
stuClass2 is class com.pangsir.model.Student
true
代碼說明:在運行期間,一個類,只有一個Class對象產生。三種方式經常使用第三種,第一種對象都有了還要反射干什麼。第二種須要導入類的包,依賴太強,不導包就拋編譯錯誤。通常都選第三種,一個字符串能夠傳入也可寫在配置文件中等多種方法。

5.3 Member & AccessibleObject

在講 Field、Method、Constructor 以前,先說說 Member 和 AccessibleObject。Member 是一個接口,表示 Class 的成員,前面的三個類都是其實現類。
AccessibleObject 是 Field、Method、Constructor 三個類共同繼承的父類,它提供了將反射的對象標記爲在使用時取消默認 Java 語言訪問控制檢查的能力。經過 setAccessible 方法能夠忽略訪問級別,從而訪問對應的內容。而且 AccessibleObject 實現了 AnnotatedElement 接口,提供了與獲取註解相關的能力。

5.4 獲取構造方法

Constructor 提供了有關類的構造方法的信息,以及對它的動態訪問的能力。

能夠經過 Class 提供的方法,獲取 Constructor 對象,具體以下:

方法返回值 方法名稱 方法說明
Constructor<T> getConstructor(Class<?>... parameterTypes) 返回指定參數類型、具備public訪問權限的構造函數對象
Constructor<?>[] getConstructors() 返回全部具備public訪問權限的構造函數的Constructor對象數組
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回指定參數類型、全部聲明的(包括private)構造函數對象
Constructor<?>[] getDeclaredConstructor() 返回全部聲明的(包括private)構造函數對象
package com.pangsir.test;

import java.lang.reflect.Constructor;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {

        // 經過Class.forName()方法得到Class對象
        Class stuClass = Class.forName("com.pangsir.model.Student");
        
        
        System.out.println("************返回全部public構造方法************");
        Constructor[] constructors = stuClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        /*
         * Output:
         * ************返回全部public構造方法************
         *    public com.pangsir.model.Student()
         *    public com.pangsir.model.Student(java.lang.String,int)
         */
    
        System.out.println("************全部的構造方法(包括:私有、受保護、默認、公有)***************");  
        Constructor[] constructors2 = stuClass.getDeclaredConstructors();
        for (Constructor constructor : constructors2) {
            System.out.println(constructor);
        }
        /*
         * Output:
         * ************全部的構造方法(包括:私有、受保護、默認、公有)***************
         *    private com.pangsir.model.Student(int)
         *    public com.pangsir.model.Student()
         *    protected com.pangsir.model.Student(char)
         *    public com.pangsir.model.Student(java.lang.String,int)
         *    com.pangsir.model.Student(java.lang.String)
         */
        
        
        Constructor constructor;

        System.out.println("************返回指定類型的 public構造器************");
        try {
            constructor = stuClass.getConstructor(String.class, int.class);
            System.out.println(constructor);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        /*
         * Output:
         * ************返回指定類型的 public構造器************
         * public com.pangsir.model.Student(java.lang.String,int)
         * 
         * 若是指定參數的構造器是非public類型的 則拋出java.lang.NoSuchMethodException異常
         */
          
        System.out.println("************返回指定類型的構造器************");
        try { 
            constructor = stuClass.getDeclaredConstructor(int.class);
            System.out.println(constructor);            // char.class
        } catch (NoSuchMethodException e) {             // String.class, int.class
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        /*
         * Output:
         * ************返回指定類型的構造器************
         * public com.pangsir.model.Student(java.lang.String,int)
         * protected com.pangsir.model.Student(char)
         * private com.pangsir.model.Student(int)
         */
        System.out.println("********獲取私有構造方法,並調用**********");  
        constructor = clazz.getDeclaredConstructor(char.class);  
        System.out.println(con);  
        //調用構造方法  
        con.setAccessible(true);//暴力訪問(忽略掉訪問修飾符)  
        obj = con.newInstance('男');  
         /*
         * Output:
         * ************返回指定類型的構造器************
         * public com.pangsir.model.Student(char)  
         * 姓名:男 
         */
    }
}

5.5 獲取變量

Field 提供了有關類或接口的單個屬性的信息,以及對它的動態訪問的能力。

能夠經過 Class 提供的方法,獲取 Field 對象,具體以下:

方法返回值 方法名稱 方法說明
Field getDeclaredField(String name) 獲取指定name名稱的(包含private修飾的)字段,不包括繼承的字段
Field[] getDeclaredField() 獲取Class對象所表示的類或接口的全部(包含private修飾的)字段,不包括繼承的字段
Field getField(String name) 獲取指定name名稱、具備public修飾的字段,包含繼承字段
Field[] getField() 獲取修飾符爲public的字段,包含繼承字段
public class Student {  
    public Student(){  
          
    }  
    //**********字段*************//  
    public String name;  
    protected int age;  
    char sex;  
    private String phoneNum;  
      
    @Override  
    public String toString() {  
        return "Student [name=" + name + ", age=" + age + ", sex=" + sex  
                + ", phoneNum=" + phoneNum + "]";  
    }  
      
      
}

測試類

package com.pangsir.field;  
import java.lang.reflect.Field;  
/* 
 * 獲取成員變量並調用: 
 *  
 * 1.批量的 
 *      1).Field[] getFields():獲取全部的"公有字段" 
 *      2).Field[] getDeclaredFields():獲取全部字段,包括:私有、受保護、默認、公有; 
 * 2.獲取單個的: 
 *      1).public Field getField(String fieldName):獲取某個"公有的"字段; 
 *      2).public Field getDeclaredField(String fieldName):獲取某個字段(能夠是私有的) 
 *  
 *   設置字段的值: 
 *      Field --> public void set(Object obj,Object value): 
 *                  參數說明: 
 *                  1.obj:要設置的字段所在的對象; 
 *                  2.value:要爲字段設置的值; 
 *  
 */  
public class Fields {  
  
        public static void main(String[] args) throws Exception {  
            //1.獲取Class對象  
            Class stuClass = Class.forName("com.pangsir.model.Student");  
            //2.獲取字段  
            System.out.println("************獲取全部公有的字段********************");  
            Field[] fieldArray = stuClass.getFields();  
            for(Field f : fieldArray){  
                System.out.println(f);  
            }
            /*
             * Output:
             * ***********獲取全部公有的字段********************  
             * public java.lang.String com.pangsir.model.Student.name  
             */
            
            System.out.println("******獲取全部的字段(包括私有、受保護、默認的)***********");  
            fieldArray = stuClass.getDeclaredFields();  
            for(Field f : fieldArray){  
                System.out.println(f);  
            }  
             /*
             * Output:
             ************獲取全部的字段(包括私有、受保護、默認的)********************  
             *  public java.lang.String com.pangsir.model.Student.name  
             *  protected int com.pangsir.model.Student.age  
             *  char com.pangsir.model.Student.sex  
             *  private java.lang.String com.pangsir.model.Student.phoneNum 
             */
            System.out.println("******獲取公有字段並調用*********");  
            Field f = stuClass.getField("name");  
            System.out.println(f);  
            //獲取一個對象  
            Object obj = stuClass.getConstructor().newInstance();//產生Student對象--》Student stu = new Student();  
            //爲字段設置值  
            f.set(obj, "劉德華");//爲Student對象中的name屬性賦值--》stu.name = "劉德華"  
            //驗證  
            Student stu = (Student)obj;  
            System.out.println("驗證姓名:" + stu.name);  
            /*
             * Output:
             *************獲取公有字段**並調用***********************************  
             * public java.lang.String com.pangsir.model.Student.name  
             * 驗證姓名:劉德華 
             */
              
            System.out.println("*****獲取私有字段****並調用***************");  
            f = stuClass.getDeclaredField("phoneNum");  
            System.out.println(f);  
            f.setAccessible(true);//暴力反射,解除私有限定  
            f.set(obj, "12345768901");  
            System.out.println("驗證電話:" + stu);  
            /*
             * Output:
             **************獲取私有字段****並調用********************************  
             * private java.lang.String fanshe.field.Student.phoneNum  
             * 驗證電話:Student [name=劉德華, age=0, sex= ,  phoneNum=12345768901]
             */
              
        }  
    }
代碼分析:調用字段時:須要傳遞兩個參數:
Object obj = stuClass.getConstructor().newInstance();

//產生Student對象--》Student stu = new Student();

//爲字段設置值
f.set(obj, "劉德華");

//爲Student對象中的name屬性賦值--》stu.name = "劉德華"
第一個參數:要傳入設置的對象,第二個參數:要傳入實參

5.6 獲取方法

Method 提供了有關類或接口的單個方法的信息,以及對它的動態訪問的能力。

能夠經過 Class 提供的方法,獲取 Field 對象,具體以下:

方法返回值 方法名稱 方法說明
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一個指定參數的Method對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法。
Method[] getDeclaredMethod() 返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或接口聲明的全部方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。
Method getMethod(String name, Class<?>... parameterTypes) 返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。
Method[] getMethods() 返回一個包含某些 Method 對象的數組,這些對象反映此 Class 對象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共 member 方法。

Student類:

public class Student {  
    //**************成員方法***************//  
    public void show1(String s){  
        System.out.println("調用了:公有的,String參數的show1(): s = " + s);  
    }  
    protected void show2(){  
        System.out.println("調用了:受保護的,無參的show2()");  
    }  
    void show3(){  
        System.out.println("調用了:默認的,無參的show3()");  
    }  
    private String show4(int age){  
        System.out.println("調用了,私有的,而且有返回值的,int參數的show4(): age = " + age);  
        return "abcd";  
    }  
}

測試類:

package com.pangsir.method;  
  
import java.lang.reflect.Method;  
  
/* 
 * 獲取成員方法並調用: 
 *  
 * 1.批量的: 
 *      public Method[] getMethods():獲取全部"公有方法";(包含了父類的方法也包含Object類) 
 *      public Method[] getDeclaredMethods():獲取全部的成員方法,包括私有的(不包括繼承的) 
 * 2.獲取單個的: 
 *      public Method getMethod(String name,Class<?>... parameterTypes): 
 *                  參數: 
 *                      name : 方法名; 
 *                      Class ... : 形參的Class類型對象 
 *      public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 
 *  
 *   調用方法: 
 *      Method --> public Object invoke(Object obj,Object... args): 
 *                  參數說明: 
 *                  obj : 要調用方法的對象; 
 *                  args:調用方式時所傳遞的實參; 
 
): 
 */  
public class MethodClass {  
  
    public static void main(String[] args) throws Exception {  
        //1.獲取Class對象  
        Class stuClass = Class.forName("fanshe.method.Student");  
        //2.獲取全部公有方法  
        System.out.println("***************獲取全部的」公有「方法*******************");   
        Method[] methodArray = stuClass.getMethods();  
        for(Method m : methodArray){  
            System.out.println(m);  
        }
        /*
         * Output:
         ***************獲取全部的」公有「方法*******************  
            public void com.pangsir.model.Student.show1(java.lang.String)  
            public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException  
            public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException  
            public final void java.lang.Object.wait() throws java.lang.InterruptedException  
            public boolean java.lang.Object.equals(java.lang.Object)  
            public java.lang.String java.lang.Object.toString()  
            public native int java.lang.Object.hashCode()  
            public final native java.lang.Class java.lang.Object.getClass()  
            public final native void java.lang.Object.notify()  
            public final native void java.lang.Object.notifyAll() 
         */
        System.out.println("***************獲取全部的方法,包括私有的*******************");  
        methodArray = stuClass.getDeclaredMethods();  
        for(Method m : methodArray){  
            System.out.println(m);  
        }  
        /*
         * Output:
         ***************獲取全部的方法,包括私有的*******************  
        public void om.pangsir.model.Student.show1(java.lang.String)  
        private java.lang.String om.pangsir.model.Student.show4(int)  
        protected void om.pangsir.model.Student.show2()  
        void om.pangsir.model.Student.show3()  
         */
        System.out.println("***************獲取公有的show1()方法*******************");  
        Method m = stuClass.getMethod("show1", String.class);  
        System.out.println(m);  
        //實例化一個Student對象  
        Object obj = stuClass.getConstructor().newInstance();  
        m.invoke(obj, "劉德華");  
         /*
         * Output:
         ***************獲取公有的show1()方法*******************  
        public void om.pangsir.model.Student.show1(java.lang.String)  
        調用了:公有的,String參數的show1(): s = 劉德華 
         */
          
        System.out.println("***************獲取私有的show4()方法******************");  
        m = stuClass.getDeclaredMethod("show4", int.class);  
        System.out.println(m);  
        m.setAccessible(true);//解除私有限定  
        Object result = m.invoke(obj, 20);//須要兩個參數,一個是要調用的對象(獲取有反射),一個是實參  
        System.out.println("返回值:" + result);
        /*
         * Output:
         ***************獲取私有的show4()方法******************  
        private java.lang.String om.pangsir.model.Student.show4(int)  
        調用了,私有的,而且有返回值的,int參數的show4(): age = 20  
        返回值:abcd 
         */
          
          
    }  
}
代碼分析:因而可知:
m = stuClass.getDeclaredMethod("show4", int.class);//調用制定方法(全部包括私有的),須要傳入兩個參數,第一個是調用的方法名稱,第二個是方法的形參類型,切記是類型。
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//須要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
System.out.println("返回值:" + result);

5.7 反射main(主方法)

Student類

package com.pangsir.main;  
  
public class Student {  
  
    public static void main(String[] args) {  
        System.out.println("main方法執行了。。。");  
    }  
}

測試類:

package fanshe.main;  
  
import java.lang.reflect.Method;  
  
/** 
 * 獲取Student類的main方法、不要與當前的main方法搞混了 
 */  
public class Main {  
      
    public static void main(String[] args) {  
        try {  
            //一、獲取Student對象的字節碼  
            Class clazz = Class.forName("fanshe.main.Student");  
              
            //二、獲取main方法  
             Method methodMain = clazz.getMethod("main", String[].class);//第一個參數:方法名稱,第二個參數:方法形參的類型,  
            //三、調用main方法  
            // methodMain.invoke(null, new String[]{"a","b","c"});  
             //第一個參數,對象類型,由於方法是static靜態的,因此爲null能夠,第二個參數是String數組,這裏要注意在jdk1.4時是數組,jdk1.5以後是可變參數  
             //這裏拆的時候將  new String[]{"a","b","c"} 拆成3個對象。。。因此須要將它強轉。  
             methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一  
            // methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二  
              
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
          
          
    }  
}

5.8 經過反射越過泛型檢查

泛型用在編譯期,編譯事後泛型擦除(消失掉)。因此是能夠經過反射越過泛型檢查的

import java.lang.reflect.Method;  
import java.util.ArrayList;  
 
/* 
* 經過反射越過泛型檢查 
*  
* 例如:有一個String泛型的集合,怎樣能向這個集合中添加一個Integer類型的值? 
*/  
public class Demo {  
   public static void main(String[] args) throws Exception{  
       ArrayList<String> strList = new ArrayList<>();  
       strList.add("aaa");  
       strList.add("bbb");  
         
   //  strList.add(100);  
       //獲取ArrayList的Class對象,反向的調用add()方法,添加數據  
       Class listClass = strList.getClass(); //獲得 strList 對象的字節碼 對象  
       //獲取add()方法  
       Method m = listClass.getMethod("add", Object.class);  
       //調用add()方法  
       m.invoke(strList, 100);  
         
       //遍歷集合  
       for(Object obj : strList){  
           System.out.println(obj);
           System.out.println(obj.getClass());
       }  
        /*
         *Output:
         * aaa
         * class java.lang.String
         * bbb
         * class java.lang.String
         * 100
         * class java.lang.Integer
         */
   }  
}

5.9 經過反射運行配置文件內容

Student類

public class Student {  
    public void show(){  
        System.out.println("is show()");  
    }  
}

配置文件以txt文件爲例子(os.properties):

className = com.pangsir.model.Student  
methodName = show

測試類:

import java.io.FileNotFoundException;  
import java.io.FileReader;  
import java.io.IOException;  
import java.lang.reflect.Method;  
import java.util.Properties;  
  
/* 
 * 咱們利用反射和配置文件,可使:應用程序更新時,對源碼無需進行任何修改 
 * 咱們只須要將新類發送給客戶端,並修改配置文件便可 
 */  
public class Demo {  
    public static void main(String[] args) throws Exception {  
        //經過反射獲取Class對象  
        Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"  
        //2獲取show()方法  
        Method m = stuClass.getMethod(getValue("methodName"));//show  
        //3.調用show()方法  
        m.invoke(stuClass.getConstructor().newInstance());  
          
    }  
      
    //此方法接收一個key,在配置文件中獲取相應的value  
    public static String getValue(String key) throws IOException{  
        Properties pro = new Properties();//獲取配置文件的對象  
        FileReader in = new FileReader("os.properties");//獲取輸入流  
        pro.load(in);//將流加載到配置文件對象中  
        in.close();  
        return pro.getProperty(key);//返回根據key獲取的value值  
    }  
}  
  /*
  *Output:
  * is show()
  */

需求:
當咱們升級這個系統時,不要Student類,而須要新寫一個Student2的類時,這時只須要更改pro.txt的文件內容就能夠了。代碼就一點不用改動

Student2類:

public class Student2 {  
    public void show2(){  
        System.out.println("is show2()");  
    }  
}

修改配置文件以下

className = com.pangsir.model.Student2  
methodName = show2

5.10 利用ParameterizedType獲取java泛型參數類

//超類
package test;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

@SuppressWarnings("unchecked")
public class Person<T> {
    private Class<T> clazz;
    public Person() {
        // 使用反射技術獲得T的真實類型
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); // 獲取當前new的對象的 泛型的父類 類型
        this.clazz = (Class<T>) pt.getActualTypeArguments()[0]; // 獲取第一個類型參數的真實類型
        System.out.println("clazz ---> " + clazz);
    }

}
//子類
package test;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Student extends Person<Student> {
}
//測試類
package test;

public class TestGetClass {

    /**
     * @param args
     */
    public static void main(String[] args) {

          Student student = new Student();
    }

}

6.反射的缺點

沒有任何一項技術是十全十美的,Java反射擁有強大功能的同時也帶來了一些反作用。

  • 性能開銷
    反射涉及類型動態解析,因此JVM沒法對這些代碼進行優化。所以,反射操做的效率要比那些非反射操做低得多。咱們應該避免在常常被執行的代碼或對性能要求很高的程序中使用反射。
  • 安全限制
    使用反射技術要求程序必須在一個沒有安全限制的環境中運行。若是一個程序必須在有安全限制的環境中運行,如Applet,那麼這就是個問題了。
  • 內部曝光
    因爲反射容許代碼執行一些在正常狀況下不被容許的操做(好比訪問私有的屬性和方法),因此使用反射可能會致使意料以外的反作用--代碼有功能上的錯誤,下降可移植性。反射代碼破壞了抽象性,所以當平臺發生改變的時候,代碼的行爲就有可能也隨着變化。
使用反射的一個原則:若是使用常規方法可以實現,那麼就不要用反射。
相關文章
相關標籤/搜索