Class對象:類型信息在運行時是如何表示的溉躲,包含了與類有關(guān)的信息,用于創(chuàng)建類的所有的“常規(guī)”對象的愧薛,Java使用Class對象來執(zhí)行其RTTI酌壕。
類是程序的一部分掏愁,每個類都有一個Class對象,被保存在一個同名的.class文件中,為了生存這個類的對象卵牍,JVM將使用“類加載器"果港。
所有類的都在在對其第一次使用時,動態(tài)加載到JVM中的糊昙。當程序創(chuàng)建第一個對類的靜態(tài)成員的引用時辛掠,就會加載這個類。這個證實構(gòu)造器也是類的靜態(tài)方法,即使在構(gòu)造器之前并沒有使用static關(guān)鍵字萝衩。因此,使用new操作符創(chuàng)建類的新對象也會被當作類的靜態(tài)成員的引用猩谊。
Class的API:###
getName():全限定的類名
getSimpleName():不包含包名的類名
getCanonicalName():全限定的類名
getInterfaces():返回所有接口的Class對象
getClassLoader():返回該類的類加載器千劈。
getComponentType():返回表示數(shù)組組件類型的 Class
getSuperclass():返回表示此 Class 所表示的實體的超類的 Class。
isArray():判定此 Class 對象是否表示一個數(shù)組類牌捷。
Class實例對象的newInstance()方法來創(chuàng)建的類墙牌,必須帶有默認構(gòu)造器。
Class對象的應(yīng)用方法:###
1.Class.forName()
2.類字面常量:類型.class
簡單暗甥,安全喜滨,因為它在編譯時就會受到檢查,不需要try語句包圍淋袖,根除了對forName()方法的調(diào)用鸿市,所以也更高效。
類字面常量不僅可以應(yīng)用于普通的類即碗,也可以應(yīng)用于接口,數(shù)組以及基本數(shù)據(jù)類型陌凳。
當使用“.class”來創(chuàng)建對Class對象的引用時剥懒,不會自動地初始化該Class對象。初始化被延遲到了對靜態(tài)方法(構(gòu)造器隱式地是靜態(tài)的)或者非常數(shù)靜態(tài)域進行首次引用時才執(zhí)行合敦。
泛化的Class引用###
Class<?>與Class等價初橘,但優(yōu)于平凡的Class,非具體的類引用
Class<? extends Number >:任何由Number派生的類
Class<? Super FancyToy>:某個類充岛,它是FancyToy超類
instanceof與Class的等價性###
instanceof和isInstance()生成的結(jié)果完全一樣保檐,且保持類型的概念
Class的equals()和==也一樣,比較實際的Class對象崔梗,沒有考慮繼承
注冊工廠###
靜態(tài)初始化器只有在類首先被加載的情況下才能被調(diào)用夜只。
使用工廠方法設(shè)計模式,將對象的創(chuàng)建工作交給類自己去完成蒜魄。工廠方法可以被多態(tài)的調(diào)用扔亥,從而創(chuàng)建適當類型的對象。
精簡版(工廠方法就是Factory接口中的create()方法):
public interface Factory<T> { T create();}
泛型參數(shù)T使得create()在每種Factory是實現(xiàn)中返回不同的類型
反射:運行時的類信息(RTTI)
編譯時谈为,編譯器必須知道所有要通過RTTI來處理的類
反射提供了一種機制——用來檢查可用的方法旅挤,并返回方法名。
運行時獲取類的信息的另一個動機:希望提供在跨網(wǎng)絡(luò)的遠程平臺上常見和運行對象的能力伞鲫,即遠程方法調(diào)用(RMI)粘茄。允許一個java程序?qū)ο蠓植嫉蕉嗯_機器上。
Class類與java.lang.reflect類庫一起對反射的概念進行支持秕脓,該類庫包含了Field柒瓣、Method以及Constructor類(每個類都是先了Member
接口)儒搭。這些類型的對象是由JVM在運行時創(chuàng)建的,用以表示未知類里對應(yīng)的成員嘹朗。
Constructor->創(chuàng)建新對象
get()和set()->讀取和修改與Field對象關(guān)聯(lián)的字段
invoke()方法->調(diào)用與Method對象關(guān)聯(lián)的方法
getField(),getMethods()和getConstructors()->返回表示字段师妙、方法以及構(gòu)造器的對象數(shù)組
匿名對象的類信息就能在運行時被完全確定下來,而在編譯時不需要知道任何信息屹培。
詳見JDK文檔
對RTTI來說默穴,編譯器在編譯時打開和檢查.class文件;而對于反射機制來說褪秀,.class文件在編譯時無法獲取蓄诽,所以在運行時打開和檢查.class文件。
類方法提取器###
瀏覽實現(xiàn)了類定義的源代碼或者其JDK文檔媒吗,只能找到在這個類定義中被定義和被覆蓋的方法仑氛。反射機制提供了一種方法,使我們能夠編寫自動展示完整接口的簡單工具闸英。
package typeinfo;
import static net.mindview.util.Print.print;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
public class ShowMethod {
private static Pattern p = Pattern.compile("\\w+\\.");
public static void main(String[] args) {
try {
Class<?> c = Class.forName("typeinfo.ShowMethod");
Method[] methods = c.getMethods();
Constructor[] ctors = c.getConstructors();
for (Method method : methods) {
print(p.matcher(method.toString()).replaceAll(""));
}
for (Constructor constructor : ctors) {
print(p.matcher(constructor.toString()).replaceAll(""));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
動態(tài)代理###
代理是基本的設(shè)計模式之一锯岖。它是你為了聽過額外或不同的操作,而插入的用來替代“實際”對象的對象甫何。這些操作通常涉及與“實際”對象的通信出吹,因此代理通常充當中間人的角色。
package typeinfo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Interface{
void doSomething();
void somethingElse(String arg);
}
class RealObject implements Interface{
public void doSomething() {
System.out.println("doSomething");
}
public void somethingElse(String arg) {
System.out.println("somethingElse " +arg);
}
}
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 {
System.out.println("*** proxy: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if (args != null) {
for (Object arg : args) {
System.out.println(" " + arg);
}
}
return method.invoke(proxied, args);
}
}
public class SimpleDynamicProxy {
public static void consumer(Interface iface){
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
RealObject real = new RealObject();
consumer(real);
Interface proxy = (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[] {Interface.class},
new DynamicProxyHandler(real));
consumer(proxy);
}
}
通過調(diào)用靜態(tài)方法Proxy.newProxyInstance()可以創(chuàng)建動態(tài)代理辙喂,這個方法需要一個類加載器(通常從已經(jīng)加載的對象中獲取其類加載器)捶牢,一個希望該代理實現(xiàn)的接口列表(不是類或抽象類),以及InvocationHandler接口的一個實現(xiàn)巍耗。
動態(tài)代理可以將所有調(diào)用重定向到調(diào)用處理器秋麸,因此通常會向調(diào)用處理器的構(gòu)造器傳遞給一個“實際”對象的引用,從而使得調(diào)用處理器在執(zhí)行其中介任務(wù)時炬太,可以將請求轉(zhuǎn)發(fā)灸蟆。
在invoke()內(nèi)部,在代理上調(diào)用方法時需要格外小心娄琉,因為對接口的調(diào)用將被重定向為對代理的調(diào)用次乓。
使用Method.invoke()將請求轉(zhuǎn)發(fā)給被代理對象,并傳入必須的參數(shù)孽水。
注釋:內(nèi)容來自《Java 編程思想》