運(yùn)行時(shí)類型信息(RTTI)使得你可以在程序運(yùn)行時(shí)發(fā)現(xiàn)和使用類型信息。
Class對(duì)象
每一個(gè)類都有一個(gè)Class對(duì)象。所有的類都是在對(duì)其第一次使用時(shí),動(dòng)態(tài)加載到JVM中。此時(shí),使用類加載器可以生成某個(gè)類的Class對(duì)象。(構(gòu)造器也是類的靜態(tài)方法。)
Class.forName("className")、classObj.getClass():這是取得Class對(duì)象引用的一種方法。
類字面量常量:另外一種方法生成Class對(duì)象的引用。eg,F(xiàn)ancyToy.class。此方法更簡(jiǎn)單,更安全,因?yàn)樵诰幾g時(shí)會(huì)受到檢查,因此不需要置于try語(yǔ)句塊中,更高效。對(duì)于基本數(shù)據(jù)類型,還有一個(gè)標(biāo)準(zhǔn)字段TYPE。為了使用類而做的準(zhǔn)備工作包含三個(gè)步驟:
1.加載:類加載器指向,查找字節(jié)碼,創(chuàng)建Class對(duì)象。
2.鏈接:驗(yàn)證字節(jié)碼,為靜態(tài)域分配存儲(chǔ)空間。若需要,解析這個(gè)類創(chuàng)建的其他類的引用。
3.初始化:若該類具有超類,對(duì)其初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊。
但需注意,使用字面量創(chuàng)建Class對(duì)象的引用時(shí),不會(huì)自動(dòng)地初始化該Class對(duì)象。初始化被延遲到對(duì)靜態(tài)方法或者非常數(shù)靜態(tài)域進(jìn)行首次引用時(shí)才執(zhí)行。
| boolean.class | Boolean.TYPE |
|---|---|
| char.class | Character.TYPE |
| byte.class | Byte.TYPE |
| short.class | Short.TYPE |
| int.class | Integer.TYPE |
| long.class | Long.TYPE |
| float.class | Float.TYPE |
| double.class | Double.TYPE |
| void.class | Void.TYPE |
泛化的Class引用:
Class intClass = int.class;
Class<Integer> gIntClass = int.class;
gIntClass = Integer.class; // Same thing
intClass = double.class;
// gIntClass = double.class //Illeagal
向上轉(zhuǎn)型可以編譯通過(guò),但是無(wú)法工作。
Class<Number> gNClass = int.class;
可以使用通配符?。
Class<?> intClass = int.class;
intClass = double.class;
還可以使用范圍通配符:
Class<? extends Number> bounded = int.class;
Class<? super Integer> s = Number.class;
使用泛型語(yǔ)法是為了提供編譯器類型檢查,使語(yǔ)言更加安全。
Class<SonClass> sClass = Son.class;
Son son = Son.newInstance();
Class<? super Son> fClass = sClass.getSuperclass();
// this won't compile
// Class<Father> fClass = sClass.getSuperclass();
// Only produces
Object obj = fClass.newInstance();
// 有點(diǎn)不懂
使用cast()轉(zhuǎn)型:
Class<House> houseType = House.class;
House h = houseType.cast(b);
RTTI形式
1)傳統(tǒng)的類型轉(zhuǎn)換——“(Shape)”。報(bào)ClassCastException。
2)代表對(duì)象的類型的Class對(duì)象。通過(guò)查詢Class對(duì)象可以獲取運(yùn)行時(shí)所需的信息。
3)instanceof,返回一個(gè)布爾值。class.isInstance(obj)提供了一種動(dòng)態(tài)地測(cè)試對(duì)象的途徑。使用此方法保持了類型的概念,表示某個(gè)類或者某個(gè)類的派生類。而使用eqauls()或者==比較Class對(duì)象時(shí)就沒有考慮繼承
if(x instanceof Dog){}
反射:運(yùn)行時(shí)的類信息
RTTI的使用條件是編譯時(shí)已知這個(gè)類。若在編譯時(shí)無(wú)法獲知這個(gè)對(duì)象所屬的類,就需要使用反射來(lái)檢查匿名對(duì)象的類的可用方法。當(dāng)然,使用反射時(shí)JVM對(duì)于目標(biāo)類的.class文件也是已知的。區(qū)別在于,對(duì)于RTTI來(lái)說(shuō),編譯器在編譯時(shí)打開和檢查.class文件,而反射則是在運(yùn)行時(shí)打開和檢查.class文件。迷惑
類方法提取器:
class.getMethods():返回Method對(duì)象數(shù)組
class.getConstructors():返回Constructor對(duì)象數(shù)組。
動(dòng)態(tài)代理
代理是為了提供額外的或不同的操作,而插入的用來(lái)代替“實(shí)際”對(duì)象的對(duì)象,是個(gè)中間人的角色。代理會(huì)提供和本體一樣的方法,執(zhí)行時(shí)沒有區(qū)別。
//動(dòng)態(tài)代理類
class DynamicProxyHandler implements InvocationHandler{
private Object proxied;
public DynamicProxyHandler(Object proxied){
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
print("do before method");
return method.invoke(proxied, args);
}
}
//執(zhí)行動(dòng)態(tài)代理
class SimpleDynamicProxy{
public static void consumer(Interface iface){
iface.doSomething();
iface.somethingElese("bonobo");
}
public static void main(String[] args){
RealObject real = new RealObject();
consumer(real);
//使用動(dòng)態(tài)代理調(diào)用
Interface proxy = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class},
new DynamicProxyHandler(real)
);
consumer(proxy);
}
}
空對(duì)象
空對(duì)象是一種設(shè)計(jì)模式,有時(shí)候我們的代碼中為避免 NullPointerException 會(huì)出現(xiàn)很多的對(duì)Null的判斷語(yǔ)句,而這些語(yǔ)句一旦多起來(lái),我們的代碼就會(huì)變的慘不忍睹,因此我們引入了空對(duì)象模式(null object pattern)以此來(lái)使我們的代碼變的更優(yōu)雅一點(diǎn)。
首先創(chuàng)建一個(gè)標(biāo)記接口:
public interface Null{}
這使得可以使用instanceof探測(cè)空對(duì)象。
class Person{
public final String name;
public final Strng address;
public Person(String name, String address){
this.name = name;
this.address = address;
}
//空對(duì)象
public static class NullPerson extends Person implements Null{
private NullPerson() {
super("None", "None");
public String toString(){ return "NullPerson";}
}
}
public static final Person NULL = new NullPerson();
}
現(xiàn)在,就可以使用Person.NULL這個(gè)單例對(duì)象作為該類的空對(duì)象。
接口與類型信息
反射可以調(diào)用所有的方法,包括private方法。首先使用class.getDeclaredMethod(methodName)獲取所有聲明的方法,然后method.setAccessible(true),然后method.invoke(obj)觸發(fā)方法。不管是內(nèi)部類還是匿名類,反射都是可以操作的。對(duì)于域來(lái)說(shuō),情況是一樣的,只是final域是修改不了的。