Java重點基礎:反射機制

一個程式設計師的成長發表於2018-12-12

一、什麼是反射?

Java反射說的是在執行狀態中,對於任何一個類,我們都能夠知道這個類有哪些方法和屬性。對於任何一個物件,我們都能夠對它的方法和屬性進行呼叫。我們把這種動態獲取物件資訊和呼叫物件方法的功能稱之為反射機制。

二、反射的三種方式

這裡需要跟大家說一下,所謂反射其實是獲取類的位元組碼檔案,也就是.class檔案,那麼我們就可以通過Class這個物件進行獲取。

1、第一種方式

Java重點基礎:反射機制

這個方法其實是Object的一個方法,Class繼承了Object,所以我們可以直接使用。

public class Test02 {

    public static void main(String[] args) {

        // 建立一個物件
        Test02 t = new Test02();

        // 獲取該物件的Class物件
        Class c = t.getClass();
        
        // 獲取類名稱
        System.out.println(c.getName()); // com.ms.Test02
    }
}
複製程式碼

2、第二種方式

public class Test02 {

    public static void main(String[] args) {

        Class c = Test02.class;

        // 獲取類名稱
        System.out.println(c.getName()); // com.ms.Test02
    }
}

複製程式碼

3、第三種

這裡需要注意,通過類的全路徑名獲取Class物件會丟擲一個異常,如果根據類路徑找不到這個類那麼就會丟擲這個異常。

public class Test02 {

    public static void main(String[] args) {

        try {
            // 根據類的全路徑名獲取
            Class c = Class.forName("com.ms.Test02");

            // 獲取類名稱
            System.out.println(c.getName()); // com.ms.Test02
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

複製程式碼

那麼這3中方式我們一般選用哪種方式呢?第一種已經建立了物件,那麼這個時候就不需要去進行反射了,顯得有點多此一舉。第二種需要匯入類的包,依賴性太強。所以我們一般選中第三種方式。

三、通過反射獲取類的構造方法、方法以及屬性

1、獲取構造方法

public class Test01 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException {

        // 載入Class物件
        Class c = Class.forName("com.reflect.User");

        System.out.println("===========================獲取所有公用的構造方法==============================");
        // 獲取所有公用的構造方法
        Constructor[] constructors = c.getConstructors();

        for (Constructor constructor : constructors) {

            System.out.println(constructor);
        }

        System.out.println("=============================獲取所有的構造方法============================");
        // 獲取所有的構造方法
        Constructor[] declaredConstructors = c.getDeclaredConstructors();

        for (Constructor declaredConstructor : declaredConstructors) {

            System.out.println(declaredConstructor);
        }

        System.out.println("=============================獲取公有 & 無參的構造方法============================");

        Constructor constructor = c.getConstructor(null);

        System.out.println(constructor);

        System.out.println("=============================獲取公有 & 有參的構造方法============================");

        Constructor constructor1 = c.getConstructor(new Class[]{String.class, Integer.class, String.class});

        System.out.println(constructor1);

        System.out.println("=============================獲取私有 & 有參 構造方法============================");

        Constructor declaredConstructor1 = c.getDeclaredConstructor(new Class[]{String.class});

        System.out.println(declaredConstructor1);
    }
}
複製程式碼

結果:

Java重點基礎:反射機制

2、獲取類屬性

public class Test02 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        // 獲取Class物件
        Class<?> clazz = Class.forName("com.reflect.User");

        System.out.println("=====獲取所有的公共欄位=====");
        Field[] fields = clazz.getFields();

        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("=====獲取所有的欄位(公開的、私有的)=====");
        Field[] declaredFields = clazz.getDeclaredFields();

        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        System.out.println("=====獲取公有欄位並使用=====");
        // 獲取指定公有欄位
        Field field = clazz.getField("name");
        // 獲取一個公有構造方法,然後例項化
        Object obj = clazz.getConstructor().newInstance();
        // 為屬性設定值
        field.set(obj, "張三");
        // 測試,看設定的值是否成功
        User user = (User) obj;

        System.out.println(user.getName());

        System.out.println("=====獲取私有欄位並使用=====");
        Field field1 = clazz.getDeclaredField("sex");

        // 獲取建構函式,例項化物件
        Object obj1 = clazz.getConstructor().newInstance();
        // 暴力反射
        field1.setAccessible(true);
        // 給屬性設定值
        field1.set(obj1, "男");
        // 測試
        User u = (User) obj1;
        System.out.println(u.getSex());
    }
}

複製程式碼

結果

Java重點基礎:反射機制

這裡需要注意,在獲取私有屬性的時候如果沒有進行暴力反射,那麼會丟擲下面這個異常。

Java重點基礎:反射機制

3、獲取類中的方法

先定義幾個方法

public void method1(String str) {

    System.out.println("public 修飾的方法");
}

private void method2() {

    System.out.println("private 修飾的方法");
}

String method3(String name, Integer age, String sex) {

    System.out.println("預設修飾 " + name + " " + sex + " " + age + "歲");

    return name + sex + age;
}

protected void method4() {

    System.out.println("protected 修飾的方法");
}

複製程式碼

正題

public class Test03 {

    public static void main(String[] args) throws Exception {

        // 獲取Class物件
        Class<?> clazz = Class.forName("com.reflect.User");

        System.err.println("======獲取所有的public修飾的方法=====");
        Method[] methods = clazz.getMethods();

        for (Method method : methods) {

            System.out.println(method);
        }

        Thread.sleep(1000);

        System.err.println("======獲取所有的方法=====");
        Method[] declaredMethods = clazz.getDeclaredMethods();

        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        Thread.sleep(1000);

        System.err.println("======獲取特定方法(帶參)並使用=====");
        Method method1 = clazz.getMethod("method1", String.class);

        System.out.println(method1);

        Thread.sleep(1000);

        System.err.println("======獲取特定方法(不帶參)並使用=====");
        Method method2 = clazz.getDeclaredMethod("method2");
        System.out.println(method2);

        System.err.println("======獲取特定方法(多個引數)並使用=====");
        Method method3 = clazz.getDeclaredMethod("method3", String.class, Integer.class, String.class);
        // 獲取構造方法,例項化一個物件
        Object obj = clazz.getConstructor().newInstance();
        // 給方法傳值
        Object invoke = method3.invoke(obj, "小濤", 24, "男");

        // 測試
        System.out.println(invoke);
    }
}

複製程式碼

結果

Java重點基礎:反射機制

Java重點基礎:反射機制

這裡需要注意的就是當一個方法需要傳入多個引數值的時候,一定要注意。踩了一點坑。

四、反射執行main方法

public class Main {

    public static void main(String[] args) {
        System.out.println("main方法執行了");
    }
}
複製程式碼

反射呼叫

public class Test04Main {

    public static void main(String[] args) {

        try {
            // 獲取Class物件
            Class<?> clazz = Class.forName("com.reflect.Main");

            // 獲取Main方法
            Method method = clazz.getMethod("main", java.lang.String[].class);

            // 呼叫
            method.invoke(null, (Object) new String[]{"a"});

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

複製程式碼

這裡需要告訴大家,在導String包的時候千萬要看清楚,我在這填了20多分鐘的坑。

Java重點基礎:反射機制

五、總結

看到這裡你已經對反射有了一個簡單的瞭解,可以使用反射獲取一些屬性方法,其實我們平時寫程式碼很少用到反射技術,但是在我們使用的一些主流框架中反射技術應用是非常廣泛的,所以學好反射也是非常有必要的。

今天就寫到這裡,下篇給大家分享一下利用反射做一些有應用型的例子。

感覺不錯就給小濤一個贊吧!


相關文章