Java 反射技術總結


所謂反射就是在程序運行期間,能夠動態獲取到類的屬性和方法,以及動態操作對象的屬性和方法。

反射技術其實應用很廣泛,尤其是各種框架技術都離不開反射,一些常用的 jar 包中間件(比如各個數據庫廠商提供的 JDBC 訪問驅動程序)也使用反射技術。之所以要總結一下反射技術,主要還是為了能夠看懂相關 Java 框架相關源碼,領悟其設計思想的巧妙之處,更好的學習和掌握各種框架。


首先我們先准備一個 Person 類,下面所有的代碼演示,都使用該類,代碼如下:

package com.jobs.reflect;

public class Person {
    //公開字段
    public String name;
    public int age;
    //私有字段
    private int salary;

    //------------------------------

    //無參構造函數
    public Person() { }

    //全參構造函數
    public Person(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    //私有構造函數
    private Person(String name, int age) {
        this.name = name;
        this.age = age;
        this.salary = 100;
        System.out.println("私有構造函數被調用...");
    }

    //------------------------------

    //私有方法
    private void method1() {
        System.out.println("私有方法 method1 被調用,無參無返回值");
    }

    public String method2(String name) {
        System.out.println("method2 被調用,有參有返回值,參數為" + name);
        return "method2 返回值為:" + name + " fastoder...";
    }
}

一、獲取一個類的 Class 對象的三種方式

為什么要獲取一個類的 Class 對象?

一個類的 Class 對象,可以理解為模板。
獲取到一個類的 Class 對象后,就相當於獲取到了一個類的模板,然后就可以進行以下操作:

  • 獲取 Class 對象(模板)中的任意屬性和方法。即使是私有屬性和方法,也能夠獲取到。
  • 通過 Class 對象(模板)創建對象實例,參照模板中的屬性和方法,可以為實例對象的屬性賦值,以及調用其方法

獲取一個類的 Class 模板對象,有三種方式:

  • 從文件中獲取,調用 Class.forName(全類名) 方法
  • 在內存中獲取,可以調用一個類的 Class 屬性
  • 對於一個實例化的對象,可以調用其 getClass() 方法

無論采用哪種方式獲取,獲取到的對象,都是同一個對象。代碼演示如下:

public class reflectDemo1 {

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

        //第 1 種方式獲取 Person 的 Class 類對象
        Class cla1 = Class.forName("com.jobs.reflect.Person");
        System.out.println(cla1);

        //第 2 種方式獲取 Person 的 Class 類對象
        Class cla2 = Person.class;
        System.out.println(cla2);

        //第 3 種方式獲取 Person 的 Class 類對象
        Person per = new Person();
        Class cla3 = per.getClass();
        System.out.println(cla3);

        //無論哪種方式獲取,獲取到的都是 Person 的同一個類對象
        System.out.println(cla1 == cla2);  //true
        System.out.println(cla1 == cla3);  //true
    }
}

二、獲取構造函數並創建實例

通過 getConstructors 方法,可以獲取到所有的 public 修飾的構造方法

通過 getDeclaredConstructors 方法,可以獲取到所有構造方法,包括 private 修飾的構造方法

通過 getConstructor(參數類型...) 方法,可以獲取到 public 修飾的具體的一個構造方法

通過 getDeclaredConstructor(參數類型...) 方法,可以獲取到具體的一個構造方法,包括私有構造方法

通過構造方法對象的 newInstance(參數值...) 方法,可以創建對象實例。

需要注意:如果想使用私有的構造方法對象創建實例對象,需要調用私有構造方法對象的 setAccessible(boolean flag) 方法,並傳入 true 即可。(true 表示取消安全訪問限制)

具體演示代碼如下:

public class reflectDemo2 {
    public static void main(String[] args) {

        try {
            //獲取 Person 的 Class 類對象
            Class cla = Class.forName("com.jobs.reflect.Person");

            System.out.println("Person類的所有構造方法為:");
            //獲取所有的構造方法,包括私有構造方法
            Constructor[] constructors = cla.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                System.out.println(constructor);
            }

            System.out.println("--------------------------");

            //獲取無參構造方法,創建實例對象
            Constructor constructor1 = cla.getConstructor();
            Person per1 = (Person) constructor1.newInstance();
            System.out.println(per1); //打印實例化對象的地址值

            //獲取全參構造方法,創建實例對象
            Constructor constructor2 = cla.getConstructor(String.class, int.class, int.class);
            Person per2 = (Person) constructor2.newInstance("侯胖胖", 40, 22000);
            System.out.println(per2); //打印實例化對象的地址值

            //獲取私有構造方法,創建實例對象
            Constructor constructor3 = cla.getDeclaredConstructor(String.class, int.class);
            //取消安全訪問限制
            constructor3.setAccessible(true);
            Person per3 = (Person) constructor3.newInstance("任肥肥", 38);
            System.out.println(per3); //打印實例化對象的地址值

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

/* 
打印出的結果如下:
Person類的所有構造方法為:
private com.jobs.reflect.Person(java.lang.String,int)
public com.jobs.reflect.Person(java.lang.String,int,int)
public com.jobs.reflect.Person()
--------------------------
com.jobs.reflect.Person@12edcd21
com.jobs.reflect.Person@34c45dca
私有構造函數被調用...
com.jobs.reflect.Person@52cc8049
*/

三、獲取字段並賦值

通過 getFields() 方法,可以獲取到所有的 public 修飾的字段

通過 getDeclaredFields() 方法,可以獲取到所有字段,包括私有字段

通過 getField(String name) 方法,可以獲取到 public 修飾的具體一個字段

通過 getDeclaredField(String name) 方法,可以獲取到具體一個字段,包括私有字段

通過 set(Object obj, Object value) 方法,給字段賦值,第一個參數是具體的實例化對象

通過 get(Object obj) 方法,獲取具體一個字段的值,該參數是具體的實例化對象

需要注意:如果想要獲取私有字段的值,以及設置私有字段的值,需要調用私有字段對象的 setAccessible(boolean flag) 方法,並傳入 true 即可。(true 表示取消安全訪問限制)

具體演示代碼如下:

public class reflectDemo3 {
    public static void main(String[] args) {

        try {
            //獲取 Person 的 Class 類對象
            Class cla = Class.forName("com.jobs.reflect.Person");

            System.out.println("Person類的所有字段為:");
            Field[] fields = cla.getDeclaredFields();
            for (Field field : fields) {
                System.out.println(field);
            }

            System.out.println("--------------------------");

            //獲取無參構造方法,創建實例對象
            Constructor cst = cla.getConstructor();
            Person per = (Person) cst.newInstance();

            //給 name 賦值
            Field fieldName = cla.getField("name");
            fieldName.set(per, "喬豆豆");

            //給 age 賦值
            Field fieldAge = cla.getField("age");
            fieldAge.set(per, 38);

            //給私有字段 salary 復制
            Field fieldSalary = cla.getDeclaredField("salary");
            //取消安全訪問限制
            fieldSalary.setAccessible(true);
            fieldSalary.set(per, 28000);

            //獲取相應字段的值
            System.out.println("Person實例化對象的字段值為:");
            System.out.println(fieldName.getName() + " ---> " + fieldName.get(per));
            System.out.println(fieldAge.getName() + " ---> " + fieldAge.get(per));
            System.out.println(fieldSalary.getName() + " ---> " + fieldSalary.get(per));

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

/*
打印出的結果如下:
Person類的所有字段為:
public java.lang.String com.jobs.reflect.Person.name
public int com.jobs.reflect.Person.age
private int com.jobs.reflect.Person.salary
--------------------------
Person實例化對象的字段值為:
name ---> 喬豆豆
age ---> 38
salary ---> 28000
*/

四、獲取方法並調用方法

通過 getMethods() 方法,可以獲取到所有 public 修飾的方法,包括繼承的方法

通過 getDeclaredMethods() 方法,可以獲取到所有方法,但是不包括繼承的方法

通過 getMethod(方法名, 參數類型...) 方法,可以獲取到具體一個 public 修飾的方法

通過 getDeclaredMethod(方法名, 參數類型...) 方法,可以獲取到具體一個方法,包括私有方法

通過 invoke(Object obj, 參數值...) 方法,可以調用具體一個方法,第一個參數是具體的實例化對象

需要注意:如果想要調用私有方法,需要調用私有方法對象的 setAccessible(boolean flag) 方法,並傳入 true 即可。(true 表示取消安全訪問限制)

具體演示代碼如下:

public class reflectDemo4 {
    public static void main(String[] args) {

        try {
            //獲取 Person 的 Class 類對象
            Class cla = Class.forName("com.jobs.reflect.Person");

            System.out.println("Person類的所有方法為:");
            Method[] methods = cla.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println(method);
            }

            System.out.println("--------------------------");

            //獲取無參構造方法,創建實例對象
            Constructor cst = cla.getConstructor();
            Person per = (Person) cst.newInstance();

            //調用私有方法,私有方法無參數
            Method method1 = cla.getDeclaredMethod("method1");
            //取消私有方法的安全訪問限制
            method1.setAccessible(true);
            method1.invoke(per);

            //調用有參數,有返回值的共有方法
            Method method2 = cla.getMethod("method2", String.class);
            String result = (String) method2.invoke(per, "任天蓬");
            System.out.println(result);

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

/*
打印出的結果如下:
Person類的所有方法為:
public java.lang.String com.jobs.reflect.Person.method2(java.lang.String)
private void com.jobs.reflect.Person.method1()
--------------------------
私有方法 method1 被調用,無參無返回值
method2 被調用,有參有返回值,參數為任天蓬
method2 返回值為:任天蓬 fastoder...
*/

到此為止,有關 Java 的一些簡單的反射技術,已經介紹完畢,以上代碼都經過測試無誤,希望能夠對大家有所幫助。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM