1. 程式人生 > >Java反射簡單學習

Java反射簡單學習

反射經常聽到這個詞,但是總是不理解這個意思。今天便來理解一下反射這個概念,為什麼說在框架設計中,反射用到的比較多。本文記錄一下學習反射方面的知識點。

反射概念

JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。概念比較精確、抽象但是不便於理解。譬如我們存在一個類,它的相關屬性、方法、構造器都是 private 型別的,對於這樣一個類,我們不能通過 new 的方式來建立一個它的物件,更不能通過平常使用方法屬性的方式來呼叫這個類的屬性、方法,甚至來建立它的物件,但是通過反射這種方式卻可以生成它的物件以及呼叫它的屬性、方法。

Class

Class 與 class 有著本質的區別。小寫的 class 是 Java 中的關鍵字,而大寫的 Class 則是類。

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
}

在 Java 的世界裡,一切皆是物件,在 Java 中存在兩種物件,一種是 new 產生的物件另一種則是 Class 物件。一般的物件,我們可以通過 new 關鍵字來產生,而 Class 物件則不可以,因為它是 JVM 生成用來儲存對應類的資訊的。也就是說,如:當我們定義了一個類 Person 時,編譯成功後,將會生成一個 Person.class 位元組碼,這個時候編譯器同時為我們從建立了一個 Class 物件並將它儲存在 Person.class 檔案中。也就是說:Person.java 編譯成 Person.class 後將會產生一個對應的Class 物件。

Class 物件獲取

一般情況下,一個例項物件對應的 Class 物件有以下三種方式獲取:

  • 1、通過例項變數的 getClass 方法:
Person person = new Person();
Class personClass = person.getClass();
System.out.println("class1: " + personClass);
  • 2、直接給出物件類檔案的.class:
Class personClass1=Person.class;
System.out.println("class2: " + personClass1);

如果不能通過 new 關鍵字來建立物件的話,我們也可以通過第三種方式來獲取:

  • 3、通過類Class的靜態方法forName():
        try {
            Class personClass2 = Class.forName("reflect.Person");
            System.out.println("class3: " + personClass2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

列印結果如下:

class1: class reflect.Person
class2: class reflect.Person
class3: class reflect.Person

Class 使用

JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。

1、Class 名稱獲取

對於 name 的獲取,Class 類提供了三種方法:

Class.getName();
Class.getSimpleName();
Class.getCanonicalName();

那麼這三種方式有何區別?通過具體的例項還說明。

首先我們得建立一個類 Person.java

package reflect;

public class Person {

    private int age;
    private String name;

    public Person() {
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

然後分別獲取它的 Class 名稱:

        //getName()方式
        Person person = new Person();
        Class personClass = person.getClass();
        System.out.println("class1: " + personClass.getName());

         //getSimpleName()方式
        Class personClass1=Person.class;
        System.out.println("class2: " + personClass1.getSimpleName());

        //getCanonicalName()方式
        try {
            Class personClass2 = Class.forName("reflect.Person");
            System.out.println("class3: " + personClass2.getCanonicalName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

列印結果如下:

class1: reflect.Person
class2: Person
class3: reflect.Person

可以看到第一種和第三種方式獲取的結果是一樣的,而只有第二種不一樣。及一三中獲取的是類的完整路徑,而第二種獲取的是類名,不包含包名。而一三種有何區別可以通過類的內部類來研究。

2、修飾符獲取

Java 中的修飾符主要有 private 、public 、static、protected、等。Java 反射提供了 API 去獲取這些修飾符。

通過 Class.getModifiers() 來獲取修飾符

        Person person = new Person();
        Class personClass = person.getClass();
        System.out.println("modifiers: " + personClass.getModifiers());

結果如下:

modifiers: 1

返回的卻是一個 int 型的數值。為什麼會返回一個整型數值呢?這是因為一個類定義的時候可能會被多個修飾符修飾,為了一併獲取,所以 Java 工程師考慮到了位運算,用一個 int 數值來記錄所有的修飾符,然後不同的位對應不同的修飾符,這些修飾符對應的位都定義在 Modifier 這個類當中。

public class Modifier {

    public static final int PUBLIC           = 0x00000001;
    public static final int PRIVATE          = 0x00000002;
    public static final int PROTECTED        = 0x00000004;
    public static final int STATIC           = 0x00000008;
    public static final int FINAL            = 0x00000010;

    ......
    public static String toString(int mod) {
        StringBuilder sb = new StringBuilder();
        int len;

        if ((mod & PUBLIC) != 0)        sb.append("public ");
        if ((mod & PROTECTED) != 0)     sb.append("protected ");
        if ((mod & PRIVATE) != 0)       sb.append("private ");

        /* Canonical order */
        if ((mod & ABSTRACT) != 0)      sb.append("abstract ");
        if ((mod & STATIC) != 0)        sb.append("static ");
        if ((mod & FINAL) != 0)         sb.append("final ");
        if ((mod & TRANSIENT) != 0)     sb.append("transient ");
        if ((mod & VOLATILE) != 0)      sb.append("volatile ");
        if ((mod & SYNCHRONIZED) != 0)  sb.append("synchronized ");
        if ((mod & NATIVE) != 0)        sb.append("native ");
        if ((mod & STRICT) != 0)        sb.append("strictfp ");
        if ((mod & INTERFACE) != 0)     sb.append("interface ");

        if ((len = sb.length()) > 0)    /* trim trailing space */
            return sb.toString().substring(0, len-1);
        return "";
    }


當然如果需要列印字串的話,可以通過 Modifier 類提供的靜態方法toString 來獲取。

System.out.println("modifiers: " + Modifier.toString(personClass.getModifiers()));

結果如下:

3、獲取 Class 的成員

一個類的成員包括屬性、方法、建構函式。對應到 Class 中就是 Field、Method、Constructor。接下來我們通過具體例項來熟悉如何獲取這些成員。

3.1、獲取 Field

獲取指定名稱的屬性 API:

public Field getDeclaredField(String name)
                       throws NoSuchFieldException,
                              SecurityException;

public Field getField(String name)
               throws NoSuchFieldException,
                      SecurityException

兩者的區別就是 getDeclaredField() 獲取的是 Class 中被 private 修飾的屬性。 getField() 方法獲取的是非私有屬性,並且 getField() 在當前 Class 獲取不到時會向祖先類獲取。

獲取所有的屬性。

//獲取所有的屬性,但不包括從父類繼承下來的屬性
public Field[] getDeclaredFields() throws SecurityException {}

//獲取自身的所有的 public 屬性,包括從父類繼承下來的。
public Field[] getFields() throws SecurityException {}
3.2、獲取 Method

Method 即是 Class 中的方法。

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

public Method getMethod(String name, Class<?>... parameterTypes)

public Method[] getDeclaredMethods() throws SecurityException

public Method getMethod(String name, Class<?>... parameterTypes)
3.3、獲取 Constructor
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

public Constructor<T> getConstructor(Class<?>... parameterTypes)

public Constructor<?>[] getDeclaredConstructors() throws SecurityException 

public Constructor<?>[] getConstructors() throws SecurityException

反射運用

以上便是簡單的獲取 Class 中的欄位、方法、建構函式,反射的機制便是操控這些欄位、方法、建構函式。

參考連結