Java重點基礎:反射機制

一、什麼是反射?

Java反射說的是在運行狀態中,對於任何一個類,我們都能夠知道這個類有哪些方法和屬性。對於任何一個對象,我們都能夠對它的方法和屬性進行調用。我們把這種動態獲取對象信息和調用對象方法的功能稱之爲反射機制。

二、反射的三種方式

這裏需要跟大家說一下,所謂反射其實是獲取類的字節碼文件,也就是.class文件,那麼我們就可以通過Class這個對象進行獲取。

1、第一種方式

這個方法其實是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);
    }
}

結果:

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());
    }
}

結果

 

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

 

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);
    }
}

結果

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

四、反射執行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技術相關的乾貨;如果你喜歡我的分享,可以用微信搜索“Java團長”或者“javatuanzhang”關注。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章