java反射的理解
初學(xué)者可能會想反射是什么,有什么作用。那我就以一個簡單的小需求開始。現(xiàn)在有3個類,A,B,C. 現(xiàn)在我想要一個方法,根據(jù)輸入的字符串,獲取相應(yīng)的實例對象,即當(dāng)我給這個對象傳入“A”的時候,我要獲取一個A實例,傳入“B”的時候獲取一個B實例,有人說用if, 那么如果有1000個類,就用1000個if或者case么,而且,如果不事先知道有多少種情況呢?要解決這個問題,我們先從基礎(chǔ)知識開始學(xué)習(xí)。
在Java程序執(zhí)行的時候,在這個進(jìn)程中,內(nèi)存分為代碼區(qū),靜態(tài)存儲區(qū),堆內(nèi)存和棧內(nèi)存。一些基本類型的變量和對象的引用變量都是在函數(shù)的棧內(nèi)存中分配。當(dāng)在一段代碼塊中定義一個變量時,java就在棧中為這個變量分配內(nèi)存空間,當(dāng)超過變量的作用域后,java會自動釋放掉為該變量分配的內(nèi)存空間,該內(nèi)存空間可以立刻被另作他用。堆內(nèi)存用于存放由new創(chuàng)建的對象和數(shù)組。在堆中分配的內(nèi)存,由java虛擬機(jī)自動垃圾回收器來管理。靜態(tài)存儲區(qū),主要是用來存儲字符串和類的靜態(tài)成員變量等。現(xiàn)在說到了對于反射來講很重要的一塊內(nèi)存,代碼區(qū)(CodeSegment)。代碼區(qū)主要存放的是加載到內(nèi)存的二進(jìn)制文件。注意,這不是對象實例,而是那些一個個還沒有被喚醒的.class文件。
從面相對象的角度來講,這些文件都是一類,都是Class類。至于如何理解.class文件對象和實例對象,我是這樣理解的。在代碼區(qū)的.class對象就像是下載視頻的種子,雖然它并不是一個實實在在的視頻,但是必須要通過這個種子才能獲得視頻文件,而且一個種子可以使用多次。相對應(yīng)的,在程序中.class文件對象,要通過這個對象來獲取需要的對象,而且能多次使用。
在Java虛擬機(jī)中,有一個很重要的搬運工的角色,就是ClassLoader,這是一大類Class,它們的作用就是把相應(yīng)的用到的二進(jìn)制的Class文件加載到內(nèi)存中。注意,這里有一個動態(tài)的加載機(jī)制,不是說,在程序啟動的時候,如果ClassPath下有100個.class文件,就會把所有的二進(jìn)制碼一下子加載到內(nèi)存中,而是剛剛開始的時候JVM使用ClassLoader把最初的幾個.class二進(jìn)制碼Load到內(nèi)存中,之后的是用到的時候才加載,慢慢的會有更多的加載到存中。當(dāng)然也可以通過代碼顯示的要求立即把相應(yīng)的二進(jìn)制類代碼加載到內(nèi)存,如JDBC驅(qū)動的加載 Class.forName("com.mysql.jdbc.Driver")
在這里有一個相關(guān)的知識點就是static靜態(tài)語句塊的問題。有人說,靜態(tài)語句塊是第一次new對象的時候執(zhí)行,這種說法是不準(zhǔn)確的,確切的說,是加載到內(nèi)存中的時候執(zhí)行。一般來講我們自己寫的類,是用的時候才加載到內(nèi)存,而我們用的方式,一般都是new一個對象,一般不會強(qiáng)制顯示的去加載,所以,大家就以為是第一次實例化的時候執(zhí)行。如果我們使用Class.Forname顯示的把它load到內(nèi)存,而并不new對象,可以發(fā)現(xiàn),此時靜態(tài)代碼塊也執(zhí)行了。
現(xiàn)在解決最初提出的那個問題,只需要一句代碼
String type = “A”;
Object o = Class.forName(type).newInstance();
下面是有關(guān)反射使用的一些常用的方法,包括獲取成員方法等。從面相對象的角度看,類中的一個個屬性或者方法都是對象,要讓其執(zhí)行,調(diào)用invoke就可以了。