Java Class類詳解

王如霜發表於2018-01-30

    最近的Java學習中又學習了一遍工廠模式,其中寫到這樣一句程式碼Class.forName(className).newInstance(),當時沒注意,但是最後程式報錯了,在除錯的過程中注意到該句有問題,於是開啟了百度之旅,這次旅途還真是收穫不少


一:Class類的簡介

       首先宣告的是Class類是Java的一個類,與我們平時自定義的類一樣,只不過名字較特殊,也是繼承了Object類,官網是這樣定義的:

public final class Class<T>
extends Object
implements Serializable, GenericDeclaration, Type, AnnotatedElement

       從程式碼中可看出,它是一個靜態類,繼承了Object類,實現了介面。
       Java程式在執行時,Java執行時系統一直對所有的物件進行所謂的執行時型別標識。這項資訊紀錄了每個物件所屬的類。JVM通常使用執行時型別資訊選準正確方法去執行,用來儲存這些型別資訊的類是Class類。Class類封裝一個物件和介面執行時的狀態,當裝載類時,Class型別的物件自動建立。
       Class 沒有公共構造方法。Class 物件是在載入類時由JVM以及通過呼叫類載入器中的 defineClass 方法自動構造的,因此不能顯式地宣告一個Class物件。
       虛擬機器為每種型別管理一個獨一無二的Class物件。也就是說,每個類(型)都有一個Class物件。執行程式時,JVM首先檢查是否所要載入的類對應的Class物件是否已經載入。如果沒有載入,JVM就會根據類名查詢.class檔案,並將其Class物件載入
       基本的 Java 型別(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也都對應一個 Class 物件。
       每個陣列屬於被對映為 Class 物件的一個類,所有具有相同元素型別和維數的陣列都共享該 Class 物件。
       一般某個類的Class物件被載入記憶體,它就用來建立這個類的所有物件。用一段程式碼來表示:

Date date1 = new Date();  
Date date2 = new Date();  
Class c1 = date1.getClass();  
Class c2 = date2.getClass();  
System.out.println(c1 == c2); // true  

       可以看出,例項化出的物件是同一個物件。因為對於相同的類,JVM只會載入一次,而與該類對應的Class物件也只會存在一個,無論該類例項化了多少物件。實上,Class物件就是用來建立類的所有的“普通”物件的。 類是程式的一部分,每個類都有一個Class物件。換言之,每當編寫並且編譯了一個新類,就會產生一個Class物件(恰當地說,是被儲存在一個同名的.class檔案中)。在執行時,當我們想生成這個類的物件時,執行這個程式的 Java虛擬機器(JVM)首先檢查這個類的Class物件是否已經載入。如果尚未載入,JVM就會根據類名查詢.class檔案,並將其載入。 一旦某個類的Class物件被載入記憶體,它就被用來建立這個類的所有(例項)物件


二:得到Class型別物件的3種方法

  1. 呼叫Object類的getClass()方法來得到Class物件,這也是最常見的產生Class物件的方法
  2. 使用Class類的中靜態forName()方法獲得與字串對應的Class物件
  3. 如果T是一個Java型別,那麼T.class就代表了匹配的類物件
public class TestClass {  
    public static void main(String[] args)  
    {  
        try {  
            // 測試Class.forName()  
            Class testTypeForName = Class.forName("TestClassType");  
            System.out.println("testForName---" + testTypeForName);  
            // 測試類名.class  
            Class testTypeClass = TestClassType.class;  
            System.out.println("testTypeClass---" + testTypeClass);  
            // 測試Object.getClass()  
            TestClassType testGetClass = new TestClassType();  
            System.out.println("testGetClass---" + testGetClass.getClass());  
        } catch (ClassNotFoundException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
}  

class TestClassType {  
    // 建構函式  
    public TestClassType() {  
        System.out.println("----建構函式---");  
    }  
    // 靜態的引數初始化  
    static {  
        System.out.println("---靜態的引數初始化---");  
    }  
    // 非靜態的引數初始化  
    {  
        System.out.println("----非靜態的引數初始化---");  
    }  

}  

執行結果:

---靜態的引數初始化---  
testForName---class TestClassType  
testTypeClass---class TestClassType  
----非靜態的引數初始化---  
----建構函式---  
testGetClass---class TestClassType  

由上面結果可看出:

  • forName()呼叫的是靜態的引數初始化;new是先呼叫非靜態的引數初始化,然後呼叫建構函式
  • 生成Class物件是一樣的,其實在JVM中只生成了一個Class物件,
  • 只列印一次“靜態的引數初始化”,靜態的方法屬性初始化時,是在載入類的時候初始化;非靜態方法是new類例項物件的時候初始化的

三:常用方法

Class.forName(字串)

  • 字串內容為:包名.類名(即完整的類路徑)
  • 返回的是一個類
  • 首先Java中任何class都要裝載在JVM上才能執行,這句話作用就是用來在JVM中查詢並載入指定的類

newInstance()

  • 建立物件
    • 上面講到Class.forName(字串)來裝載類的,都知道類在使用時必須建立物件後才能使用,該句話用來建立物件的
  • 與new的區別
    • 直觀看來它是一個方法,new是一個關鍵字
    • new建立一個類時,這個類可以沒有被載入,但是可能需要classloader來載入;newInstance()方法必須保證該類已載入,而且已連線了
    • new是動態載入,newInstance()靜態載入
    • new是強型別,效率教高,能呼叫任何public構造;newInstance()是弱型別,效率較低,只能呼叫無參構造

四:應用情景

Java的Class類是Java反射機制的基礎,通過Class類可獲得關於這個類的相關資訊

  • 載入資料庫驅動時
JdbcConfig jdbcConfig = XmlConfigReader.getInstance().getJdbcConfig();
//載入驅動  
Class.forName(jdbcConfig.getDriverName());
conn = DriverManager.getConnection(jdbcConfig.getUrl(),jdbcConfig.getUserName(),jdbcConfig.getPassword());
  • 使用工廠模式時
    使用工廠模式時,通常會根據讀取到的配置檔案裡的資訊來建立具體的工廠,然後通過該具體工廠建立具體的產品,當想使用別的工廠生產別的產品時,只需改一下配置檔案即可,這樣提高了軟體的可伸縮性、可擴充套件性
String className = XmlConfigReader.getInstance().getDaoFactory("item-dao-factory");
ItemDaoFactory factory = null;
//例項化一個工廠
factory = (ItemDaoFactory)Class.forName(className).newInstance();

總結

  • 可以說工廠模式學了很多遍了,但是這一次的學習又有了新認識,看來知識要不斷應用
  • 理解明白一些底層程式碼還是非常重要的,明白了執行機制,感覺思路順暢了好多

相關文章