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 中的字段、方法、構造函數,反射的機制便是操控這些字段、方法、構造函數。

如何通過反射來操作類的相關屬性,請查看其他相關文章《細說反射,Java 和 Android 開發者必須跨越的坎》

參考鏈接

1、http://blog.csdn.net/sinat_38259539/article/details/71799078
2、http://www.importnew.com/21235.html
3、http://blog.csdn.net/briblue/article/details/74616922

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章