反射的概念 和基本使用(一)

word發表於2022-04-05

1 反射

什麼是反射

java反射 是指在執行狀態中 對於任意一個類 我們都可以知到這個類的所有方法和屬性 也可以呼叫其所有的方法和屬性 這種動態獲取的方式 我們稱為 反射

什麼是class物件

我們通過使用反射 就是通過Class類來實現的 Class 類的例項表示正在執行的 Java 應用程式中的類和介面。

也就是jvm中有N多的例項每個類都有該Class物件。(包括基本資料型別)

反射的使用

獲取class物件的三種方式

基本類
/**
 *
 * @author : look-word
 * @date : 2022-04-05 20:49
 **/
public class Student {
    private String username;
    private String gender;

    public  String getInfo() {
        this.setUsername("張三");
        this.setGender("男");
        return this.username+"="+this.gender;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}
測試程式碼如下
/**
 * @author : look-word
 * @date : 2022-04-05 20:54
 **/
public class Test {
    public static void main(String[] args) {

        /*
            1 建立物件 獲取其class物件
         */
        Student student = new Student();
        Class<? extends Student> aClass = student.getClass();
        System.out.println(aClass.getSimpleName());
        /*
            2 任何資料型別(包括基本資料型別)都有一個“靜態”的class屬性
         */
        Class<? extends Student> bClass=Student.class;
        System.out.println(aClass == bClass?"兩者是同一物件":"兩者不是同一物件");
        /*
            3 通過Class類的forName方法獲取
         */
        try {
            //  Class.forName(類的相對路徑)
            Class<?> cClass = Class.forName("bean.Student");
            System.out.println(bClass == cClass?"兩者是同一物件":"兩者不是同一物件");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
結果如下

證明

在執行期間 一個類只有一個class物件的產生

常用的是第三次 清晰 明瞭 因為 在一個專案中 可能會產生 相同名字的類

解決疑惑 為什麼要是用 第三種

第一種物件都有了還要反射干什麼。

第二種需要匯入類的包,依賴太強,不導包就拋編譯錯誤。

第三種,一個字串可以傳入也可寫在配置檔案中等多種方法。

獲取Class類中的所有構造方法

基本類
public class Student {
    //---------------構造方法-------------------
    //(預設的構造方法)
    Student(String str){
        System.out.println("(預設)的構造方法 s = " + str);
    }

    //無參構造方法
    public Student(){
        System.out.println("呼叫了公有、無參構造方法執行了。。。");
    }

    //有一個引數的構造方法
    public Student(char name){
        System.out.println("姓名:" + name);
    }

    //有多個引數的構造方法
    public Student(String name ,int age){
        System.out.println("姓名:"+name+" 年齡:"+ age);//這的執行效率有問題,以後解決。
    }

    //受保護的構造方法
    protected Student(boolean n){
        System.out.println("受保護的構造方法 n = " + n);
    }

    //私有構造方法
    private Student(int age){
        System.out.println("私有的構造方法   年齡:"+ age);
    }
}
測試程式碼
/**
 * 測試構造方法
 * @author : look-word
 * @date : 2022-04-05 21:18
 **/
public class TestConstructor {

/**
 * 通過Class物件可以獲取某個類中的:構造方法、成員變數、成員方法;並訪問成員;
 *
 * 1.獲取構造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"構造方法
            public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、預設、公有)

 * 		2).獲取單個的方法,並呼叫:
 * 			public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、預設、公有;
 *
 * 			呼叫構造方法:
 * 			Constructor-->newInstance(Object... initargs)
*/
    public static void main(String[] args) throws Exception {
        Class<?> student = Class.forName("bean.Student");
        /*
            1 獲取所有共有的構造方法
         */
        System.out.println("\n1 獲取所有共有的構造方法");
        Constructor<?>[] constructors = student.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }

        /*
            2 獲取共有的無參構造方法 可以寫 null 或者 不填
         */
        System.out.println("\n2 獲取共有的無參構造方法 可以寫 null 或者 不填");
        Constructor<?> constructor2 = student.getConstructor();
        constructor2.newInstance();

        /*
            3 獲取 給定引數共有的構造方法 public bean.Student(java.lang.String,int)
         */
        System.out.println("\n 3 獲取 給定引數共有的構造方法 public bean.Student(java.lang.String,int)");
        Constructor<?> constructor3 = student.getConstructor(String.class, int.class);
        constructor3.newInstance("張三",19);

        /*
            4 獲取 私有給定引數的構造方法 私有 不給定引數 不傳引數即可
         */
        Constructor<?> constructor4 = student.getDeclaredConstructor(int.class);
        /*
            獲取私有的屬性 或者構造方法是 需要 設定無障礙 俗稱 暴力訪問
            不設定 會出異常 java.lang.IllegalAccessException
         */
        constructor4.setAccessible(true);
        constructor4.newInstance(19);
    }
}

執行結果

注意

獲取私有屬性的時候 一定要設定無障礙

setAccessible(true);

不設定 會出異常 java.lang.IllegalAccessException

newInstance(Object... initargs) 建立一個新例項
使用此 Constructor 物件表示的構造方法來建立該構造方法的宣告類的新例項,並用指定的初始化引數初始化該例項。

每次是用 newInstance建立的物件 都是不同的物件 代表不同的例項

操作成員變數

基本類
 
public class Student {
	public Student(){
		
	}
	//**********欄位*************//
	public String name;
	protected int age;
	char sex;
	private String phoneNum;
	
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", sex=" + sex
				+ ", phoneNum=" + phoneNum + "]";
	}
}
測試程式碼
/**
 * @author : look-word
 * @date : 2022-04-05 21:55
 **/
public class TestField {

/*
 * 獲取成員變數並呼叫:
 *
 * 1.批量的
 * 		1).Field[] getFields():獲取所有的"公有欄位"
 * 		2).Field[] getDeclaredFields():獲取所有欄位,包括:私有、受保護、預設、公有;
 * 2.獲取單個的:
 * 		1).public Field getField(String fieldName):獲取某個"公有的"欄位;
 * 		2).public Field getDeclaredField(String fieldName):獲取某個欄位(可以是私有的)
 *
 * 	 設定欄位的值:
 * 		Field --> public void set(Object obj,Object value):
 * 					引數說明:
 * 					1.obj:要設定的欄位所在的物件;
 * 					2.value:要為欄位設定的值;
*/
    public static void main(String[] args) throws Exception{
        Class<?> aClass = Class.forName("bean.Student");
        Student student = new Student();

        /*
            獲取所有的共有欄位
         */
        System.out.println("-------------------獲取所有的共有欄位--------------------");
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            /*
                filed => public java.lang.String bean.Student.username
                filed => 成員變數
             */

            /*
               我理解為 給某個物件的 成員變數 賦值
               當前的filed 為 username 因為這裡只能獲取為 共有屬性的成員變數
             * field.set(student,"zhangsan");
             */
            field.set(student,"zhangsan");
            System.out.println(student);
            /*
                field.get(student);
                獲取某student中的 field 的 內容
             */
            Object o = field.get(student);
            System.out.println(o);
            /*
             *  列印filed的內容 => public java.lang.String bean.Student.username
             */
            System.out.println(field);
        }

        System.out.println("-------------------給私有欄位賦值--------------------");
        Field phone = aClass.getDeclaredField("phone");
        phone.setAccessible(true);// 設定無障礙 
        phone.set(student,"110");// 賦值給student物件

        System.out.println("-------------------獲取私有欄位--------------------");
        Field phone1 = aClass.getDeclaredField("phone");
        phone1.setAccessible(true);// 設定無障礙
        System.out.println(phone1.get(student));//取出student物件中的phone屬性的值
    }
}
注意

在操作私有屬性的時候 不管是獲取還是設定值 都需要設定無障礙

setAccessible(true);// 設定無障礙

相關文章