1. 程式人生 > 其它 >01、Java特性--反射

01、Java特性--反射

反射

基本概念

反射機制:將類的各個組成部分封裝為其他物件,這就是反射機制。

通俗來講,反射就是來操作Java類的一種機制,通過反射可以操作Java中的位元組碼檔案。

反射的核心類是Class類,它是類的類模板,即該類是用來描述Java類的。

描述的基本上就是一個類的組成部分:包名、類名、介面、父類、成員變數(Filed)、構造方法(Construct)、普通方法(Method)、註解(Annotation)等等。
所以說,反射的操作基本上就是對這些類的元素進行操作。

Java在計算機中會經歷三個階段,分別如下:

Source原始碼階段:*.java檔案會被編譯成*.class位元組碼檔案。

Class物件階段:*.class位元組碼檔案會被類載入器裝進記憶體,並將其封裝成Class物件。

RunTime執行階段:建立物件的過程。

其中,在Class物件階段,Class物件將原位元組碼中所有成員變數封裝成Field[],將建構函式封裝成Construction[],將成員方法封裝成Method[]。

獲取Class物件

獲取Class物件分別對應Java程式碼在計算機中的三個階段:

1、Source原始碼階段:多用於配置檔案的讀取和載入。

Class.forName("全類名")。

2、Class類物件階段:多用於引數的傳遞。

**.class

3、Runtime執行階段:多用於物件的獲取位元組碼的方式

xxx.getClass();

注意:同一個位元組碼檔案(*.class)在一次程式執行過程中,只會被載入一次,無論通過哪一種方式獲取的Class物件都是同一個。

案例分析

下面的案例演示三種Java位元組碼的獲取方式:

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式一:Class.forName("全類名")
        Class clazz1 = Class.forName("com.legend.reflect.Person");
        System.out.println("clazz1 = " + clazz1);

        // 方式二:類名.class
        Class clazz2 = Person.class;
        System.out.println("clazz2 = " + clazz2);

        // 方式三:物件.getClass()
        Class clazz3 = new Person().getClass();
        System.out.println("clazz3 = " + clazz3);

        boolean isEquals = (clazz1 == clazz2) && (clazz2 == clazz3) ? true : false;
        System.out.println("clazz1 == clazz2 == clazz3:" + isEquals);
    }
}

輸出結果如下所示:

clazz1 = class com.legend.reflect.Person
clazz2 = class com.legend.reflect.Person
clazz3 = class com.legend.reflect.Person
clazz1 == clazz2 == clazz3:true

Class物件功能

Class物件包含一個類的所有資訊,這裡只介紹主要的。

(1)獲取成員變數

Field[] getFields() :獲取所有public修飾的成員變數
Field getField(String name)   獲取指定名稱的 public修飾的成員變數

Field[] getDeclaredFields()  獲取所有的成員變數,不考慮修飾符
Field getDeclaredField(String name) 

(2)獲取建構函式

Constructor<?>[] getConstructors()  
Constructor<T> getConstructor(Class<?>... parameterTypes)  

Constructor<?>[] getDeclaredConstructors()  
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 

(3)獲取成員方法

Method[] getMethods()  
Method getMethod(String name, Class<?>... parameterTypes)  

Method[] getDeclaredMethods()  
Method getDeclaredMethod(String name, Class<?>... parameterTypes)

(4)獲取全類名

String getName() 

(5)獲取類載入器

ClassLoader	getClassLoader()

Filed成員變數

下面針對Person物件中的三種修飾符:private、protected和public三種類型的成員變數使用反射賦值。

1、定義Person物件,並提供三種修飾符修飾的屬性。

public class Person {
    public String name;
    protected int age;
    private String address;

	...get和set...
}

2、使用獲取成員變數的方式進行反射,只能獲取到public的成員變數。

public class Main {
    public static void main(String[] args) throws NoSuchFieldException {
        Class personClass = Person.class;
        // 獲取所有public修飾的成員變數
        Field[] fields = personClass.getFields();
        for (Field field : fields){
            System.out.println(field);
        }
    }
}

獲取所有的變數,必須使用getDeclaredFields()方法來完成。

public class Main {
    public static void main(String[] args) throws NoSuchFieldException {
        Class personClass = Person.class;
        Field[] fields = personClass.getDeclaredFields();
        for (Field field : fields){
            System.out.println(field);
        }
    }
}

注意:如果獲取單個private或protected屬性的值需要使用暴力反射來完成:setAccessible(true);

Constructor建構函式

通過反射的方式來建立物件,使用到的實體類如下:

public class Person {
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
	...get和set...
}

然後通過反射的方式來建立該物件:

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
        Constructor[] constructors = personClass.getConstructors();
        for (Constructor constructor : constructors){
            System.out.println(constructor);
        }

        // 獲取有參的建構函式來建立物件
        Constructor constructor = personClass.getConstructor(String.class, Integer.class);
        Object legend = constructor.newInstance("Legend", 30);
        System.out.println(legend);
    }
}

如果要獲取private修飾的建構函式,需要使用getDeclaredConstructor方法。

Method方法物件

通過反射呼叫物件中的方法,所用到的例項類如下:

public class Person {
    private String name;
    private Integer age;

    public Person() {
    }
	...get和set...

    public void eat(String food){
        System.out.println("eat..." + food);
    }
}

然後通過反射的方式來執行eat方法:

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
        // 獲取所有public的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods){
            System.out.println(method);
        }

        // 執行位元組碼中的方法
        Object o = personClass.newInstance();
        Method method = personClass.getMethod("eat", String.class);
        method.invoke(o, "apple");
    }
}

反射的大致用法如上所示,還可以通過反射的方式去修改配置類並執行。