反射簡介
反射允許我們在程序運(yùn)行時(shí)獲取和使用類的信息。
Class 對(duì)象
Java程序運(yùn)行時(shí),用Class對(duì)象表示類型信息,它包含了與類相關(guān)的信息。類是程序的一部分,每個(gè)類都有一個(gè)Class對(duì)象,也就是說,當(dāng)一個(gè)類被編譯之后,就會(huì)產(chǎn)生一個(gè)Class對(duì)象,并保存在.class文件中。當(dāng)程序創(chuàng)建第一個(gè)對(duì)類的靜態(tài)成員的引用時(shí),JVM就會(huì)用類加載器加載Class對(duì)象,當(dāng)該類的Class對(duì)象被加載入內(nèi)存后,就可以使用它來創(chuàng)建實(shí)例對(duì)象。
獲取Class對(duì)象引用的方式:
- 通過Class.forName() 方法來加載類并獲取Class對(duì)象的引用并初始化該類,該方法的參數(shù)必須為類的全限定名(包含包名)
try {
Class.forName("Person");
} catch (Exception ex) {
ex.printStackTrace();
}
- 如果已經(jīng)獲得該類的實(shí)例,可通過該實(shí)例調(diào)用getClass()方法獲取Class對(duì)象的引用
Person person = new Person();
Class clazz = person.getClass();
- 使用類字面常量,該方式僅僅只是拿到Class對(duì)象的引用,并沒有進(jìn)行初始化
Class clazz = Person.class;
對(duì)于基本數(shù)據(jù)類型,同樣存在類字面常量,而且,在對(duì)應(yīng)的包裝類中有一個(gè)字段TYPE作為基本數(shù)據(jù)類型的類字面常量的引用,比如,對(duì)于 int 來說,其字面常量為int.class,它與Integer.TYPE等價(jià),但要注意的是,它們與Integer.class是兩回事。
為了使用類而做的準(zhǔn)備工作有三個(gè)步驟:
1.加載。由類加載器執(zhí)行,查找字節(jié)碼,并通過字節(jié)碼創(chuàng)建一個(gè)Class對(duì)象。
2.鏈接。在這個(gè)階段將驗(yàn)證類中的字節(jié)碼,為靜態(tài)域分配存儲(chǔ)空間,如果必須的話,還要解析這個(gè)類創(chuàng)建的對(duì)其他類的引用
3.初始化。如果該類有超類,則對(duì)其初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊。初始化被延遲到了對(duì)靜態(tài)方法(在這種情況下,構(gòu)造器可以視為靜態(tài)的方法)或者非常數(shù)靜態(tài)域進(jìn)行首次引用的時(shí)候才執(zhí)行。例子如下:
import java.util.Random;
class Initable {
static final int staticFinal = 47;
static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
}
class Initable2 {
static int staticNonFinal = 147;
static {
System.out.println("Initializing Initable2");
}
}
class Initable3 {
static int staticNonFinal = 74;
static {
System.out.println("Initializing Initable3");
}
}
public class ClassInitialization {
public static Random rand = new Random(47);
public static void main(String[] args) throws Exception {
//這里不會(huì)觸發(fā)初始化
Class initable = Initable.class;
System.out.println("After creating Initable ref");
//這里也不會(huì)觸發(fā)初始化,因?yàn)橹皇且镁幾g期常量
System.out.println(Initable.staticFinal);
//這里觸發(fā)初始化,staticFinal2并不是一個(gè)編譯期常量
System.out.println(Initable.staticFinal2);
//這里同樣會(huì)觸發(fā)初始化, 因?yàn)槿绻鹲tatic域不是final的情況下,對(duì)它進(jìn)行訪問時(shí)必須要先進(jìn)行鏈接(分配存儲(chǔ)空間)和初始化(初始化存儲(chǔ)空間)
System.out.println(Initable2.staticNonFinal);
//Class.forName()方法在加載類后會(huì)進(jìn)行初始化
Class.forName("Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
}
輸出結(jié)果如下:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
Class對(duì)象一些常用方法:
- newInstance()
創(chuàng)建新實(shí)例對(duì)象,使用該方法來創(chuàng)建的類,必須帶有默認(rèn)的構(gòu)造器,否則會(huì)拋出異常InstantiationException - getName(), getSimpleName(), getCanonicalName()
getName() 獲取全限定的類名,getSimpleName() 獲取不含包名的類名, getCanonicalName() 也是獲取全限定的類名, 對(duì)于普通的類來說,兩者結(jié)果基本是一樣的,但是對(duì)于一些特殊的類,比如數(shù)組和內(nèi)部類等,兩者的結(jié)果有所差異。
public class Main {
public static void main(String[] args) {
Integer[] array = new Integer[5];
InnerClass innerClass = new InnerClass();
System.out.println(array.getClass().getName());
System.out.println(array.getClass().getCanonicalName());
System.out.println(innerClass.getClass().getName());
System.out.println(innerClass.getClass().getCanonicalName());
}
private static class InnerClass {
}
}
結(jié)果如下:
[Ljava.lang.Integer;
java.lang.Integer[]
Main$InnerClass
Main.InnerClass
- isInstance()
和instanceof關(guān)鍵字作用一樣,但是更靈活,instanceof需要顯示地指定類型,而isInstance()則不需要,只要拿到Class對(duì)象的引用即可。
對(duì)于兩個(gè)Class對(duì)象的比較,可以使用==也可以使用equals(),但是它們與isIntanceof以及isInstance()有所不同,==和equals()不考慮繼承
class Base {}
class Derived extends Base {}
public class ClassCompareTest {
static void test(Object x) {
Class clazz = x.getClass();
String className = clazz.getSimpleName();
System.out.println("測試" + clazz);
System.out.println("x instanceof Base [" + (x instanceof Base) + "]");
System.out.println("x instanceof Derived [" + (x instanceof Derived) + "]");
System.out.println("Base.class.isInstance(x) [" + (Base.class.isInstance(x)) + "]");
System.out.println("Derived.class.isInstance(x) [" + (Derived.class.isInstance(x)) + "]");
System.out.println(className + ".class == Base.class [" + (clazz == Base.class) + "]");
System.out.println(className + ".class == Derived.class [" + (clazz == Derived.class) + "]");
System.out.println(className + ".class.equals(Base.class) [" + (clazz.equals(Base.class)) + "]");
System.out.println(className + ".class.equals(Derived.class) [" + (clazz.equals(Derived.class)) + "]");
}
public static void main(String[] args) {
test(new Base());
test(new Derived());
}
}
輸出結(jié)果為:
測試class Base
x instanceof Base [true]
x instanceof Derived [false]
Base.class.isInstance(x) [true]
Derived.class.isInstance(x) [false]
Base.class == Base.class [true]
Base.class == Derived.class [false]
Base.class.equals(Base.class) [true]
Base.class.equals(Derived.class) [false]
測試class Derived
x instanceof Base [true]
x instanceof Derived [true]
Base.class.isInstance(x) [true]
Derived.class.isInstance(x) [true]
Derived.class == Base.class [false]
Derived.class == Derived.class [true]
Derived.class.equals(Base.class) [false]
Derived.class.equals(Derived.class) [true]
類成員
通過Class對(duì)象可以獲取類運(yùn)行時(shí)的類成員信息,類成員用Member接口表示,該接口的實(shí)現(xiàn)類有Field、Method、 Constractor。
Field類
- Field對(duì)象的獲取
Field表示類的成員變量,可以使用Class對(duì)象的getFields()和getDeclaredFields(),兩者的區(qū)別在于前者只能拿到所有從父類繼承來的public成員變量以及自己的public成員變量,而后者只能拿在本類內(nèi)聲明的所有成員變量,不管修飾符是什么。另外還有getField()和getDeclaredField()用來返回特定的成員變量,區(qū)別和前面一樣。對(duì)于Method類以及Constractor類也同理。 - 獲取成員變量的類型
可以使用getType()和getGenericType()獲取成員變量的類型,兩者的區(qū)別:- 前者返回的是變量類型的Class對(duì)象引用,不包含泛型信息
- 當(dāng)變量類型沒有泛型時(shí),后者結(jié)果與前者一致。當(dāng)變量類型有泛型時(shí),返回結(jié)果是一個(gè)Type接口的類型,包含泛型信息。
假設(shè)有這樣一個(gè)類:
運(yùn)行下面例子:public class Student { private String name; private List<String> courses; }Class clazz = Student.class; Field[] fields = clazz.getDeclaredFields(); for (Field field: fields) { System.out.println("getType(): " + field.getType()); System.out.println("getGenericType(): " + field.getGenericType()); }
結(jié)果如下:
getType(): class java.lang.String
getGenericType(): class java.lang.String
getType(): interface java.util.List
getGenericType(): java.util.List<java.lang.String>
參考:《Java編程思想(第4版)》Bruce Eckel 著