天天看點

【Java——反射】1、反射:架構的靈魂2、擷取Class對象3、Class對象功能4、反射機制的應用案例

反射

  • 1、反射:架構的靈魂
    • 1.1 概述
  • 2、擷取Class對象
    • 2.1 擷取Class對象方式
  • 3、Class對象功能
    • 3.1 擷取功能(部分,具體參考api文檔)
      • 1、擷取成員變量
      • 2、擷取構造方法
      • 3、擷取成員方法
      • 4、擷取全類名
    • 3.2 java.lang.reflect.Field:成員變量
    • 3.3 java.lang.reflect.Method:方法對象
  • 4、反射機制的應用案例
    • 需求
    • 實作
    • 思路
    • 代碼實作

1、反射:架構的靈魂

1.1 概述

  • 架構:半成品軟體。可以在架構的基礎上進行軟體開發,簡化編碼
  • 反射:将類的各個組成部分封裝為其他對象,這就是反射機制;學習使用架構并不需要了解反射,但是如果想要自己寫一個架構,那麼就需要對反射機制有很深入的了解。

    好處:

    1. 可以在程式運作過程中,操作這些對象。

    2. 可以解耦,提高程式的可擴充性。

在學習反射以前,我們先來了解一下Java代碼在計算機中所經曆的三個階段:

【Java——反射】1、反射:架構的靈魂2、擷取Class對象3、Class對象功能4、反射機制的應用案例

1、Source源代碼階段:.java被編譯成*.class位元組碼檔案。

2、Class類對象階段:.class位元組碼檔案被類加載器加載進記憶體,并将其封裝成Class對象(用于在記憶體中描述位元組碼檔案),Class對象将原位元組碼檔案中的成員變量抽取出來封裝成數組Field[],将原位元組碼檔案中的構造函數抽取出來封裝成數組Construction[],将成員方法封裝成數組Method[]。當然Class類内不止這三個,還封裝了很多,我們常用的就這三個。

3、RunTime運作時階段:使用new建立對象的過程。

2、擷取Class對象

2.1 擷取Class對象方式

  1. Class.forName(“全類名”):将位元組碼檔案加載進記憶體,傳回Class對象

    * 多用于配置檔案,将類名定義在配置檔案中。讀取檔案,加載類

    2. 類名.class:通過類名的屬性class擷取

    * 多用于參數的傳遞

    3. 對象.getClass():getClass()方法在Object類中定義着。

    * 多用于對象的擷取位元組碼的方式

* 結論:
	同一個位元組碼檔案(*.class)在一次程式運作過程中,隻會被加載一次,不論通過哪一種方式擷取的Class對象都是同一個。
           

代碼實作:

//方法一:對象.getClass()
public void test01(){
        Person p = new Person();
        Class<? extends Person> aClass = p.getClass();
        System.out.println(aClass);
    }
    
//方法二:類名.class
public void test02(){
        Class<Person> personClass = Person.class;
        System.out.println(personClass);
    }
           
test01和test02擷取類對象,都需要依賴api Person
//方法三:Class.forName("全類名");
  public void test03() throws ClassNotFoundException {
        //手動加載Person.class檔案進記憶體
        Class aClass = Class.forName("net.zretc.reflect.Person");//傳入Person的路徑
        System.out.println(aClass);
    }
           

3、Class對象功能

3.1 擷取功能(部分,具體參考api文檔)

1、擷取成員變量

Field[] getFields()          //擷取所有public修飾的成員變量
Field getField(String name)  //擷取指定名稱的public修飾的成員變量

Field[] getDeclaredFields()  //擷取所有的成員變量,不考慮修飾符
Field getDeclaredField(String name)  //擷取指定的成員變量,不考慮修飾符
           

2、擷取構造方法

Constructor<?>[] getConstructors() //擷取所有public修飾的構造函數
Constructor<T> getConstructor(類<?>... parameterTypes)  //擷取指定的public修飾的構造函數

Constructor<?>[] getDeclaredConstructors()  //擷取所有的構造函數,不考慮修飾符
Constructor<T> getDeclaredConstructor(類<?>... parameterTypes)  //擷取指定的構造函數,不考慮修飾符
           

3、擷取成員方法

Method[] getMethods()           //擷取所有public修飾的成員方法
Method getMethod(String name, 類<?>... parameterTypes) //擷取指定名稱的public修飾的成員方法

Method[] getDeclaredMethods()  //擷取所有的成員方法,不考慮修飾符
Method getDeclaredMethod(String name, 類<?>... parameterTypes) //擷取指定名稱的成員方法,不考慮修飾符
           

4、擷取全類名

String getName() 
           

3.2 java.lang.reflect.Field:成員變量

  • 操作:
    1. 設定值 void set(Object obj, Object value)
    2. 擷取值 Object get(Object obj)
    3. 忽略通路權限修飾符的安全檢查 setAccessible(true):暴力反射

Field的get/set方法舉例代碼:

測試實體類:

class SupClass{
    public String gendar;
}
public class Person {
    public String name;
    byte[] b;
    private int age;
    public void show(){
        System.out.println("show------null----");
    }
    public void show(String name,int age){
        System.out.println("show------name---age----");
    }
    private void sup(){
        System.out.println("sup---");
    }
}
           

測試類:

public void test06() throws Exception {
        Class pClass = Class.forName("net.zretc.reflect.Person");
        Field name_field = pClass.getDeclaredField("name");//私有變量
        Person person = (Person) pClass.newInstance();//執行個體化對象

        //設定通路權限:暴力反射
        name_field.setAccessible(true);

        Object name_value = name_field.get(person);//person.getName()
        System.out.println(name_value);
    }
    
           
public void test05() throws Exception {
        Class pClass = Class.forName("net.zretc.reflect.Person");
        Field age_field = pClass.getField("age");
        
        //執行個體化Person
        Person person = (Person) pClass.newInstance();//newInstance相當于執行的空參數的構造器

        //設定值
        age_field.set(person,20);               //person.setAge(20);

        //檢視age變量的值
        Object age_value = age_field.get(person);       //  person.getAge()

        System.out.println(age_value);
    }
           

3.3 java.lang.reflect.Method:方法對象

  • 執行方法:Object invoke(Object obj, Object… args) 沒有傳回值,為null
  • 擷取方法名稱:String getName:擷取方法名

    代碼展示

public void test08() throws Exception {
        //類對象
        Class pClass = Class.forName("net.zretc.reflect.Person");
        //執行個體化Person
        Person person = (Person) pClass.newInstance();

        Method test = pClass.getDeclaredMethod("test");//私有化方法

        //暴力反射
        test.setAccessible(true);
        test.invoke(person);

        //show方法對象
        Method show = pClass.getMethod("show", String.class, int.class);
        //執行show方法: person.show("hehe",30)
        Object result = show.invoke(person, "hehe", 30);
        System.out.println(result);//沒有傳回值,為null
    }
           

4、反射機制的應用案例

需求

寫一個"架構",在不改變該類的任何代碼的前提下,可以幫我們建立任意類的對象,并且執行其中的任意方法。

實作

(1)配置檔案

(2)反射機制

思路

(1)将需要建立的對象的全類名和需要執行的方法定義在配置檔案中

(2)在程式中加載讀取配置檔案

(3)使用反射技術把類檔案加載進記憶體

(4)建立對象

(5)執行方法

代碼實作

實體類:

Person 類

public class Person {
    //無參方法
    public void eat(){
        System.out.println("eat...");
    }
}

           

Student類

public class Student {
    //無參方法
    public void study(){
        System.out.println("I am a Student");
    }
}
           

配置檔案編寫:

className =zretc.edu.cn.Person
methodName = eat
           

代碼:

public class ReflectTest {
    public static void main(String[] args) throws Exception {
    
        //1.加載配置檔案
        //1.1建立Properties對象
        Properties pro = new Properties();
        //1.2加載配置檔案
        //1.2.1擷取class目錄下的配置檔案(使用類加載器)
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream("pro.properties");
        pro.load(inputStream);

        //2.擷取配置檔案中定義的資料
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        //3.加載該類進記憶體
        Class cls = Class.forName(className);
        //4.建立對象
        Object obj = cls.newInstance();
        //5.擷取方法對象
        Method method = cls.getMethod(methodName);
        //6.執行方法
        method.invoke(obj);
    }
}