動態的創建Class對象方法及調用方式性能分析

  • 2021 年 4 月 15 日
  • 筆記

有了Class對象,能做什麼?

  • 創建類的對象:調用Class對象的newInstance()方法

    • 類必須有一個無參數的構造器。

    • 類的構造器的訪問許可權需要足夠。

思考?沒有無參的構造器就不能創建對象嗎?只要在操作的時候明確的調用類中的構造器,並將參數傳遞進去之後,才可以實例化操作。

  • 如下

    • 通過Class類的getDeclaredConstructor(Class … parameterTypes)取得本類的指定形參類型的構造器

    • 向構造器的形參中傳遞一個對象數組進去,裡面包含了構造器中所需的各個參數

    • 通過Constructor實例化對象

調用指定的方法

通過反射,調用類中的方法,通過Method類完成。

  • 通過Class類的getMethod(String name,Class…parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型。

  • 之後使用Object invoke(Object obj,Object[] args)進行調用,並向方法中傳遞要設置的obj對象的參數資訊。

Object invoke(Object obj, Object … args)

  • Object對應原方法的返回值,若原方法無返回值,此時返回null。

  • 若原方法若為靜態方法,此時形參Object obj可為null。

  • 若原方法形參列表為空,則Object[] args為null。

  • 若原方法聲明為private,則需要在調用此invoke()方法前,顯示調用方法對象的setAccessible(true)方法,將可訪問private的方法。

setAccessible

  • Method和Field、Constructor對象都有setAccessible()方法。

  • setAccessible作用是啟動和禁用訪問安全檢查的開關。

  • 參數值為true則指示反射的對象在使用時應該取消Java語言訪問檢查。

    • 提高反射的效率。如果程式碼中必須用反射,而該句程式碼需要頻繁的被調用,那麼請設置為true。

    • 使得原本無法訪問的私有成員也可以訪問。

  • 參數值為false則指示反射的對象應該實施Java語言訪問檢查。

package com.chao.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//動態的創建對象,通過反射
public class Test09 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //獲得Class對象
        Class c1 = Class.forName("com.chao.reflection.User");
        //構造一個對象
        //User user = (User)c1.newInstance(); //本質是調用了類的無參構造器
        //System.out.println(user);

        //通過構造器創建對象
        //Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        //User user2 = (User)constructor.newInstance("追夢王子", 001, 18);
        //System.out.println(user2);

        //通過反射調用普通方法
        User user3 = (User)c1.newInstance();
        //通過反射獲取一個方法
        Method setName = c1.getDeclaredMethod("setName", String.class);

        //invoke : 激活的意思
        //(對象,"方法的值")
        setName.invoke(user3,"追夢王子");
        System.out.println(user3.getName());

        //通過反射操作屬性
        System.out.println("8888888888888888888888888888888888");
        User user4 = (User)c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //不能直接操作私有屬性,我們需要關閉程式的安全檢測,屬性或者方法的setAccessible(true).
        name.setAccessible(true);
        name.set(user4,"追夢王子2");
        System.out.println(user4.getName());

    }
}

調用方式的性能分析—-程式碼

package com.chao.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//分析性能問題
public class Test10 {
    //普通方式調用
    public static void test01(){
        User user = new User();
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("普通方式執行10億次:"+(endTime-startTime)+"ms");
    }
    //反射方式調用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("反射方式執行10億次:"+(endTime-startTime)+"ms");
    }
    //反射方式調用 關閉檢測
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("關閉檢測執行10億次:"+(endTime-startTime)+"ms");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        test02();
        test03();
    }
}