理解java.lang.Class類

逸卿發表於2014-04-15

Java Class類理解:

首先,Class是一個java類,跟Java API中定義的諸如Thread、Integer類、我們自己定義的類是一樣,也繼承了Object(Class是Object的直接子類)。總之,必須明確一點,它其實只是個類,只不過名字比較特殊。更進一步說,Class是一個java中的泛型型別。

 

對於我們自己定義的類,我們用類來抽象現實中的某些事物,比如我們定義一個名稱為Car的類來抽象現實生活中的車,然後可以例項化這個類,用這些例項來表示我的車、你的車、黃的車、紅的車等等。

好了,現在回到Class 類上來,這個類它抽象什麼了?它的例項又表示什麼呢?

在一個執行的程式中,會有許多類和介面存在。我們就用Class這個來來表示對這些類和介面的抽象,而Class類的每個例項則代表執行中的一個類。例如,執行的程式有A、B、C三個類,那麼Class類就是對A、B、C三個類的抽象。所謂抽象,就是提取這些類的一些共同特徵,比如說這些類都有類名,都有對應的hashcode,還有其他一些後設資料。

需要注意的是,這個特殊的Class類沒有公開的建構函式,那怎麼獲取Class類的例項呢?有幾個途徑。

1.      當Java虛擬機器載入一個類的時候,它就會自動建立一個Class類的例項來表示這個類。例如,虛擬機器載入Car這個來的時候,它就會建立一個Class類的例項。然後可以通過以下方法獲得這個Class物件:

java.lang.ClassclassObj = ClassName.class;

2. 可以通過呼叫類載入器(ClassLoader)的defineClass()方法來得到一個例項。這個方法接受一個byte陣列,載入這個byte陣列否成的class類,同時例項化一個Class物件。

3. ClassName.class( )   ClassName.getClass( )

現在來分析一下Class類的原始碼(java.lang.Class):

public final

    class Class<T> implementsjava.io.Serializable,

                            java.lang.reflect.GenericDeclaration,

                             java.lang.reflect.Type,

                             java.lang.reflect.AnnotatedElement {…

 

ClassClass<T>,前一個Class表示這是一個類的宣告,第二個Class是類的名稱,<T>表示這是一個泛型類,帶有引數T.同時,Class類實現了許多介面。

 

緊接著定義了幾個靜態變數:

private static final int ANNOTATION= 0x00002000;

private static final int ENUM      = 0x00004000;

private static final int SYNTHETIC = 0x00001000;

 

接著定義一個本地方法registerNatives(),並在靜態塊中呼叫:

private static native void registerNatives();

    static {

        registerNatives();

}

 

私有的建構函式:

private Class() {}

訪問修飾符是private,程式設計師是無法直接呼叫這個建構函式,只能通過JVM來呼叫它,構造一個Class例項。

  public String toString() {

        return (isInterface() ? "interface " : (isPrimitive() ? "" : "class"))

            + getName();

}

這是Class物件例項的字串表示方法,應該不陌生。那麼,它返回什麼東西呢?

>如果這個Class物件例項所表示的是一個Java類,則返回class full_classname.

例如java.lang.Math.java這個類,它所對應的Class例項的toString方法返回的就是class java.lang.Math

>如果是介面,將class改成interface。還有一種特殊情況,如果Class例項表示的是void型別,則發揮void。如果是基本型別,一樣的返回基本型別的名稱。

 

靜態方法forName:

public static Class<?> forName(String className)

                throws ClassNotFoundException{

        return forName0(className,true, ClassLoader.getCallerClassLoader());

}

根據給定的類名引數className,查詢與className相對應的Class例項,然後載入、連線該例項物件,之後返回這個Class例項。其中例如以下程式碼段將輸出:

class java.lang.Thread

public class ClassTest {

 

    /**

     * @param args

     */

    public static void main(String[] args) {

      

       try {

           System.out.println( Class.forName("java.lang.Thread") );

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

           System.out.println("No ClassNamed java.lang.Thread");

       }

 

    }

 

}

 

forName方法過載:

    public static Class<?> forName(String name, boolean initialize,

                                   ClassLoaderloader)

        throws ClassNotFoundException

    {

        if (loader == null) {

            SecurityManager sm = System.getSecurityManager();

            if (sm != null) {

                ClassLoader ccl = ClassLoader.getCallerClassLoader();

                if (ccl != null) {

                    sm.checkPermission(

                        SecurityConstants.GET_CLASSLOADER_PERMISSION);

                }

            }

        }

        return forName0(name, initialize,loader);

    }

注意到這個forName過載方法中多了兩個方法引數,其中initialize這個boolean型別指定是否要初始化對應的Class例項,loader指定載入Class例項的載入器。留意這個方法可能丟擲的異常還是比較多的,比如連線失敗、找不到對應的類等等。

 

forName0本地方法:

private static native Class<?> forName0(String name, boolean initialize,

                                            ClassLoaderloader)

        throws ClassNotFoundException;

這是一個本地方法,在前面的靜態方法forName的兩個版本中都呼叫了這個本地方法。

 

newInstance()方法:

public T newInstance()          // T是個泛型引數,是Class例項所表示的Java類

        throws InstantiationException,IllegalAccessException

    {

        if (System.getSecurityManager()!= null) {

            checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());

        }

        return newInstance0();     //這是一個本地方法,也在Class類中定義

}

注意這是一個例項方法,必須由Class類的例項物件呼叫。例如,有一個代表java.lang.Thread類的Class例項物件objec1,也就是說,泛型引數T此時就是Thread,object1這個例項代表Thread這個類。好了,現在呼叫object1的newInstance方法,即object1.newInstance(),此時這個呼叫將返回一個Thread類的物件。簡單驗證:

public class ForName {

 

    /**

     * @param args

     * @throwsIllegalAccessException

     * @throwsInstantiationException

     */

    public static void main(String[] argsthrows InstantiationException, IllegalAccessException {

       Class<?> c = null ;

       try {

           c = Class.forName("java.lang.Thread");

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       }

       Thread thread = (Thread) c.newInstance(); //型別轉化一下

       System.out.println(thread.getId());

 

    }

 

}

在我機子中,上述程式碼輸出 8 。即c.newInstance產生一個ID為8的新執行緒。

 

getClassLoader:

public ClassLoader getClassLoader() {

        ClassLoader cl = getClassLoader0();

        if (cl == null)

            return null;   // Bootstrap

        SecurityManager sm = System.getSecurityManager();

        if (sm != null) {

            ClassLoader ccl = ClassLoader.getCallerClassLoader();

            if (ccl != null && ccl != cl&& !cl.isAncestor(ccl)) {

               sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);

            }

        }

        return cl;

    }

 

    //Package-private to allow ClassLoader access

    native ClassLoadergetClassLoader0();   // 本地方法

這個方法返回該Class物件代表的類的類載入器。如果類的載入器是Bootstrap,則返回null。下面的程式碼輸出:The ClassLoader of Thread Class is Bootstrap

 

Class<?> classObj= Thread.class;

       ClassLoader loader = classObj.getClassLoader();

       if (loader == null) {

           System.out.println("TheClassLoader of Thread Class is Bootstrap");

       } else {

           System.out.println(loader);

       }

 

獲取父類方法:getSuperclass()

public native Class<? super T> getSuperclass();

這是一個本地方法,這裡的邏輯有點饒,方法返回的是這個Class物件所代表的Java類的父類對應的的Class 物件。

例如: Thread.class.getSuperclass()將返回一個代表Thread類的Class物件,Thread.class.getSuperclass().toString()則輸出這個Class物件的字串表示:classjava.lang.Object。其實這裡的關係無非就是說Thread的超類是Object。只是饒了Class物件這個彎子,至於這麼繞有什麼好處,還沒有深刻體會。

 

(持續更新中)

相關文章