一、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