第十四章-類型信息(1)

一、Class對象

      Class對象是一種特殊的對象,它包含了與類有關的信息。事實上,Class對象就是用來創建類的所有“常規”對象的。每個類都有一個Class對象,換言之,每當編寫並編譯了一個新類,就會產生一個Class對象(保存在一個同名的.class文件中)。爲了生成這個類的對象,運行這個程序的Java虛擬機將使用“類加載器”。

      所有的類都是在對其第一次使用時,動態加載到JVM中的。當程序創建第一個對類的靜態成員引用時,就會加載這個類。這證明了構造器也是類的靜態方法,即使在構造器之前並沒有使用static關鍵字。因此,使用new創建類的新對象也會被當作對類的靜態成員的引用。

      類加載器首先檢查這個類的Class對象是否已經加載。如果尚未加載,默認的類加載器就會根據類名查找.class文件。在這個類的字節碼被加載時,它們就會接收驗證,以確保其沒有被破壞,並且不包含不良的Java代碼。

      一旦某個類的Class對象被載入內存,它就被用來創建這個類的所有對象。

      Class對象中,forName()是取得Class對象引用的一種方法。它是用一個包含目標類的文本名(區分大小寫)的String作爲輸入參數,返回的是該類的Class對象的引用,使用該方法會顯式的要求JVM加載該類的.class文件並初始化該類。

class A
{
	static
	{
		System.out.println("From A!");
	}
}

class B extends A
{
	static
	{
		System.out.println("From B!");
	}
}

public class Test 
{
	public static void main(String[] args)
	{
		System.out.println("---------Using 'new'---------");
		//創建對象之前,根據繼承關係,需要首先加載A.class,然後加載B.class
		B b = new B();
		System.out.println("-----------------------------");
		
		System.out.println("---Using 'Class.forname()'---");
		try
		{
			//顯式要求加載B.class,但是B.class已經加載,所以不會重新加載
			Class.forName("B");
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		System.out.println("-----------------------------");
	}
}

輸出

---------Using 'new'---------
From A!
From B!
-----------------------------
---Using 'Class.forname()'---
-----------------------------

 

class A
{
	static
	{
		System.out.println("From A!");
	}
}

class B extends A
{
	static
	{
		System.out.println("From B!");
	}
}

public class Test 
{
	public static void main(String[] args)
	{

		System.out.println("---Using 'Class.forname()'---");
		try
		{
			//顯式要求加載B.class,發現B.class和A.class都沒加載
			//所以根據繼承關係會先加載A.class然後加載B.class
			Class.forName("B");
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		System.out.println("-----------------------------");
		
		System.out.println("---------Using 'new'---------");
		//B.class已經加載,可以創建對象
		B b = new B();
		System.out.println("-----------------------------");
	}
}

 

輸出

---Using 'Class.forname()'---
From A!
From B!
-----------------------------
---------Using 'new'---------
-----------------------------

 

二、類字面常量

      Java提供了另一種方法來生成對Class對象的引用,即使用“類字面常量”:例如B.class。

      這樣做不僅更簡單,而且更安全,因爲它在編譯時就會受到檢查(因此不需try&catch),並且它根除了對forName()方法的調用,所以更高效。

      需要注意的是,當使用該方式創建對Class對象的引用時,不會自動初始化該Class對象。爲了使用類而做的準備工作實際上包含三個步驟:

      1.加載,這是由類加載器執行的。該步驟將查找字節碼,並從這些字節碼中創建一個Class對象。

      2.連接。在連接階段將驗證類中的字節碼,爲靜態域分配存儲空間,並且如果必須的話,將解析這個類創建的對其他類的所有引用。

      3.初始化。如果該類有超類,則對其初始化,執行靜態初始化器和靜態初始化塊。

       使用類字面常量時初始化被延遲到了對靜態方法(構造器隱式地是靜態的)或者非常數靜態域進行首次引用時才執行。

class A
{
	static
	{
		System.out.println("From A!");
	}
}

class B extends A
{
	static
	{
		System.out.println("From B!");
	}
}

public class Test 
{
	public static void main(String[] args)
	{
		System.out.println("-----using '.class'------");
		//只加載.class而不初始化,所以無輸出
		Class c = B.class;
		System.out.println("-------------------------");

		System.out.println("--using Class.forName()--");
		try
		{
			//雖然已經加載,但是還未初始化,因此會初始化
			Class.forName("B");
		}
		catch (Exception e) 
		{
			e.printStackTrace();
		}
		System.out.println("-------------------------");
	}
}

輸出

-----using '.class'-----
-------------------------
--using Class.forName()--
From A!
From B!
-------------------------

 

三、instanceof

      instanceof的語法:[某個對象] instanceof [某個類或接口]

      它返回一個布爾值,用來告訴我們某個對象是否是某個類的實例,或者是某個類的子類的實例;當第二個參數是接口時,表示某個對象是否是該接口的某個實現的實例。它考慮了類的繼承體系。

class A
{}

class B extends A
{}

interface C
{}

class D implements C
{}

public class Test 
{
	public static void main(String[] args)
	{
		B b = new B();
		System.out.println("b instance of B: " + (b instanceof B));
		System.out.println("b instance of A: " + (b instanceof A));
		
		D d = new D();
		System.out.println("d instance of D: " + (d instanceof D));
		System.out.println("d instance of C: " + (d instanceof C));
	}
}

輸出

b instance of B: true
b instance of A: true
d instance of D: true
d instance of C: true

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章