Java筆記-反射

天會晴發表於2020-10-01

反射:

通過java語言的反射機制可以操作位元組碼檔案。
在java.lang.reflect.*;的包下。

反射機制重要的類:

java.lang.Class; //代表整個位元組碼檔案
java.lang.reflect.Method; //代表位元組碼中的方法位元組碼
java.lang.reflect.Constructor; //代表位元組碼中的構造方法位元組碼
java.lang.Field; //代表位元組碼中的屬性位元組碼。類中的成員變數,包括靜態變數、例項變數。

java.lang.Class:

獲取java.lang.Class的方式:
1.用Class.forName()來獲取:1.靜態方法。2.方法的引數是一個字串。3.字串需要是一個完整類名。4.完整類名帶有包名。
2.用getClass()方法來獲取:任何一個物件都有getClass()方法。返回當前物件方法區的位元組碼檔案。
3.用class屬性來獲取:java語言中任何一種型別,包括基本資料型別,都有class屬性。

  • 可以通過獲得當前的Class檔案(c),使用c.newInstance()來建立物件。通過反射機制建立物件。此時,只需要修改
    配置檔案中的資訊,就可以實現建立不同物件的功能。
  • 使用Class.forName(“完整類名”); 會導致類載入,這樣可以只執行這個類的靜態程式碼塊。

java.lang.Field:

通過 類名.getFields() 可以得到field陣列,陣列中是類中所有public修飾的屬性。
通過得到的field.getName(),方法可以得到屬性名。
類名.getDeclaredFields(),可以獲得類中所有屬性。
field.getType(),可以得到當前屬性的型別,返回一個class,可以通過field.getType().getName()獲得型別名字。
getModifiers(),class中的方法,可以返回此類或介面的修飾符,返回的是int型別,
可以通過Modifiers中的toString(int mod) 方法,將返回的int型別資料轉換為對應的修飾符。

使用反射機制去訪問一個物件的屬性:

Class studentClass = Class.forName("reflesh.student");
        Object obj = studentClass.newInstance();//obj是student物件,底層呼叫無參構造方法。

        //獲取名字為no的屬性
        Field noField = studentClass.getField("no");

        //給obj物件的no屬性賦值222.
        noField.set(obj,222);

//讀取obj物件的no屬性的值。
        noField.get(obj);

注意:以上訪問只能訪問public型別的,private型別的需要用Field類中的方法**setAccessible(true)**來打破封裝,才可以獲取。

java.lang.reflect.Method:

通過 類名.getDeclaredMethods() 返回一個Methods陣列,陣列中存放物件的所有方法。
Method.getName();//返回方法的名字
Method.getModifiers();//返回修飾符列表,返回的是數值
Method.toString();//通常將Method.getModifiers()返回的數值傳進去,返回修飾符列表。
Method.getReturnType();//可以獲得方法的返回值型別。可配合.getSimpleName()使用。
Method.getParameterTypes();//可以獲得引數型別陣列,通過getSimpleName()方法可以看到屬性。

使用反射機制去呼叫一個物件的方法(重點):

例如:

public static void main(String[] args) throws Exception {
        Class student = Class.forName("reflesh.student");//獲取類
        Object obj = student.newInstance();//用類來new物件
        Method method = student.getDeclaredMethod("login",String.class,String.class);//獲取物件的方法
        Object result = method.invoke(obj,"admin","123");//將方法和這個物件繫結,傳參。
    }

反射機制讓程式碼具有通用性,可變化的內容寫到配置檔案中。修改配置檔案可以建立不同的物件和方法,java程式碼不需要修改。

java.lang.reflect.Constructor:

通過類名.getDeclaredConstructors();獲得一個Constructor陣列,裡面存放物件的所有構造方法。
通過constructor.getModifiers();獲得構造方法的修飾符
通過constructors.getParameterTypes();可以獲得當前構造方法的引數型別陣列

用反射調構造方法:

無引數構造方法:

Class student = Class.forName("reflesh.student");
        Object obj = student.newInstance();

有引數構造方法:

Class student = Class.forName("reflesh.student");
Constructor con = student.getDeclaredConstructor(String.class,String.class);
con.newInstance("sun","6225");

路徑問題:

解決通常情況下,程式碼交換位置出現的路徑問題,前提條件:檔案必須在類路徑(在src下的都是類路徑)下。

String path = Thread.currentThread().getContextClassLoader().getResource("test.properties").getPath();
Thread.currentThread()當前執行緒物件。
getContextClassLoader執行緒物件的方法,可以獲得當前執行緒的類載入器物件。
getResource()獲得資源,類載入器的方法,當前執行緒的類載入器預設從根路徑下載入資源。

也可以在類載入器中使用getResourceAsStream()方法得到流。
例如:

InputStream reader =  Thread.currentThread().getContextClassLoader().getResourceAsStream("test.properties");

資源繫結器:

使用這種方式時,配置屬性檔案必須放在類路徑下。並且只能繫結xx.properties檔案。
在寫路徑的時候,路徑後面的副檔名不能寫。

ResourceBundle bundle = ResourceBundle.getBundle("test");
        String className = bundle.getString("ClassName");

類載入器:

專門負責載入類的命令/工具。ClassLoader
JDK自帶的3個類載入器:
1.啟動類載入器:
程式碼在開始執行前,會將所需要的類全部載入到JVM中。首先通過啟動類載入器載入.class檔案,專門載入核心類庫(rt.jar)。
2.擴充套件類載入器:
如果通過啟動類載入器載入不到時,會通過擴充套件類載入器載入。擴充套件類載入器專門載入ext目錄中的檔案。
3.應用類載入器:
如果擴充套件類載入器也沒有找到,會通過應用類載入器載入,應用類載入器專門載入classpath中的jar包。

雙親委派機制:

為了保證類載入的安全,優先從啟動類載入器中載入(父),再從擴充套件類載入器中載入(母),如果都載入不到
才會考慮從應用類載入器中載入。

可變長度引數:

在方法的引數中,型別的後面加…可以實現。
例如:public static void add(int… args)
可以將可變長度引數當作一個陣列來看。
例如:

public static void main(String[] args) {
        reflesh3.printString("abc","efg","ghj");//這裡也可以直接傳一個陣列
    }
    public static void printString(String...args){
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
    }

注意:可變長引數只能有1個,且必須在引數列表的最後。

如何獲得一個類的父類:

通過getSuperclass()來獲取父類。
例如:

public static void main(String[] args) throws Exception{
        Class stringClass = Class.forName("java.lang.String");
        Class superClass = stringClass.getSuperclass();
        System.out.println(superClass.getSimpleName());
    }

如何獲得一個類的所有介面:

通過getInterfaces()方法來獲得一個陣列,陣列中存放物件的所有介面。
例如:

 public static void main(String[] args) throws Exception{
        Class stringClass = Class.forName("java.lang.String");
        Class[] interfaces = stringClass.getInterfaces();
        for(Class in : interfaces){
            System.out.println(in.getSimpleName());
        }  
    }

相關文章