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 中的欄位、方法、建構函式,反射的機制便是操控這些欄位、方法、建構函式。