1. 程式人生 > 實用技巧 >Java中反射機制的理解

Java中反射機制的理解

  1. java反射的概念
    • 在Java中的反射機制是指在執行狀態中,對於任意一個類都能夠知道這個類所有的屬性和方法;並且對於任意一個物件,都能夠呼叫它的任意一個方法;這種動態獲取資訊以及動態呼叫物件方法的功能成為Java語言的反射機制。簡單的說,反射機制就是在程式的執行過程中被允許對程式本身進行操作,比如自我檢查,進行裝載,還可以獲取類本身,類的所有成員變數和方法,類的物件,還可以在執行過程中動態的建立類的例項,通過例項來呼叫類的方法,這就是反射機制一個比較重要的功能了。
  2. 理解反射機制,首先要理解類的載入過程
    • 載入過程圖
    • 在Java程式執行的時候,要經歷三個步驟:載入、連線和初始化。首先程式要載入到JVM的方法區中,然後進行連線,最後初始化。這裡就主要介紹一下類的載入。如上圖,首先,JVM會從硬碟中讀取Java原始檔並將其載入到方法區中同時生成類名.class檔案,也就是類物件,這個類物件中包含了我們建立類的例項時所需要的模板資訊,也就是原始碼中的成員變數和方法等。Class本身也是一個類,它的主要功能之一就是生成類載入時的class檔案,為類的初始化及例項化做準備。而我們在程式中通過關鍵字new建立的物件建立的是類的物件,而不是類物件,二者的區別如圖中所示。

  3. 原理模型
  4. 常用的反射類以及用法
    • java.lang.class
      • (呼叫某個物件的getClass()方法
        User user=new User()
        Class clazz=user.getClass()

      • 呼叫某個類的class屬性來獲取該類對應的Class物件
        Class clazz=User.class

      • 呼叫包的地址
        Calss clazz=Class.forName("")

    • java.lang.reflect.Method
      • 獲取類的所有方法資訊getDeclaredMethods()

      • 獲取類的公有方法資訊getMethods()

      • 要注意的是,在暴力獲取私有方法後還要獲取呼叫該方法的許可權:.setAccessible(true)(預設false,需要設定成true)

    • java.lang.reflect.fields
      • 獲取類的所有成員屬性資訊getDeclaredFields()

      • 獲取類的公有成員屬性資訊getFields()

    • java.lang.reflect.constructor
      • 獲取類的所有構造方法資訊getDeclaredConstructors()

      • 獲取類的公有構造方法資訊getConstructors()

  5. 建立例項的兩種方式
    • 使用Class物件的newInstance()方法來建立該Class物件對應類的例項,但是這種方法要求該Class物件對應的類有預設的 空構造器。

    • 先使用Class物件獲取指定的Constructor物件,再呼叫Constructor物件的newInstance()方法來建立 Class物件對應類的實 例,通過這種方法可以選定構造方法建立例項。

  6. Java反射優缺點
    • Java反射優點:在執行期確定物件、繫結物件、操作物件,最大限度的發揮了Java的靈活性。

    • Java反射缺點:反射相當於一系列解釋操作,通知jvm要做的事情,效能相對較低。反射會跳過型別檢查等,導致安全性問題。例如通過反射跳過泛型的編譯前型別檢查

  7. 應用場景
    • 框架中的xml,properties等配置

    • 框架中註解的使用

    • 動態代理、AOP

    • JDBC資料庫連線

  8. 例子
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import com.javasm.lang.Person;
    
    public class PersonTest3 {
    
        /**
         * 操作類的屬性/成員變數
         * 操作方法---->操作Method類
         * 操作構造---->操作Constructor類
         */
        public static void test1() {
            try {
                Person person = new Person();
                //1.建立Class類的例項
                Class clazz = Class.forName("com.lj.bean.Person");//類或者介面的全限定名稱  包名+類名
                Method[] methods = clazz.getMethods();//只獲得public方法(包含從父類繼承的)
                System.out.println(Arrays.toString(methods));
    
                methods = clazz.getDeclaredMethods();//只獲得本類裡面public/protected/預設/private
                System.out.println(Arrays.toString(methods));
                // String 方法名     parameterTypes:引數型別  可變引數 >=0實參
                //對name賦值  setName
                Method method = clazz.getMethod("setName", String.class);
    
                //執行方法
                Object obj = method.invoke(person, "張三");//方法的引數資料
    
                System.out.println(obj);
                System.out.println("person:"+person.getName());
    
                System.out.println("------------------------------");
                //執行b方法
                method = clazz.getDeclaredMethod("b", String.class,Integer.class);
                method.setAccessible(true);
    
                obj = method.invoke(person, "hello world",100);
    
                System.out.println(obj);
            } catch (ClassNotFoundException|NoSuchMethodException|SecurityException e) {
                e.printStackTrace();
            } catch (IllegalAccessException |IllegalArgumentException |InvocationTargetException e) {
                e.printStackTrace();
            } 
    
        }
        public static void main(String[] args) {
    
            test1();
        }
    }