Java中關於先有雞還是先有蛋的問題----Class&Object


 在Java中,我們常常會看到一個類型:Class。並且在類似Person.class,cache.getClass()等代碼中見到它的身影。

眾所周知,Class是用來描述一個類的類型,而Object是所有對象的最終父對象。那么就會引申出下邊的兩個結論:

1、如果從對象的角度來看,那么肯定是先有Object對象,其次才有其派生的對象Class。

2、Class表示的是類、對象,肯定是先有類這個概念,其次才有各個類型(抽象的、非抽象的),包括Object。

這就會出現一個問題,到底是先有Class(雞)還是先有Object(蛋)?

 

好吧,此處先給出答案,是先有Object,然后才有Class的。

原因是Object,是所有對象的最終父對象,而Class本身也是一個對象。所以是先有Object,然后才Class對象的。

那么如何解釋第二點呢? 這是因為一個概念被混淆了。

在Java中,所有的對象都派生自Object,而Class類(注意此處是大寫也是一個類)所以他也繼承自Object,這個我們可以在eclipse里邊通過查看類的繼承關系清楚的看到。

在Java中,還有一個class(注意此處是小寫)。他表示的是一個個(防盜連接:本文首發自http://www.cnblogs.com/jilodream/ )對象,也就是一個個類。Object是這些對象的其中之一。同時在這些對象中有一個對象,它的作用是用來識別標記其它對象的內容,這個類叫做Class(注意此處是大寫)。 因此就會出現有一個class的名字叫做Class。而問題中將class等價於Class,很顯然是不合理的。兩者完全不在一個維度里。

但是問題還是會出現,在加載Object時(尚未完成加載時),究竟如何實現為其加載對應的Class的呢?這個就涉及到對象最初是如何被系統加載的。這里JVM啟動時使用的是C++代碼對這些最初的核心類進行表示。分配好內存空間后,互相建立引用,進而才完成類的初始化。所以可以看到如果從JVM實現的角度來說,二者是同時完成加載的。ps 而且面相對象的語言遇到類似的問題,通常也都是通過自舉的形式解決最初系統加載順序的問題(此處藍色字體感謝@之奇一昂的糾正,原文有偏差已修改)

 

拋磚引玉----深入學習Class類

解了class,Object,Class的關系,我們接下來深入說說Class類。(這才是這篇博客的主要目的)

 一、背景知識

類對象在使用之前都會被JVM加載(其實是經過加載、連接、初始化三個步驟對類完成初始化)。類加載指的就是JVM將class文件讀入內存,並為之創建一個Class對象。同時當一個類被加載后,再次使用時,就不會被重復加(防盜連接:本文首發自http://www.cnblogs.com/jilodream/ )載。這樣新建的Class與加載的class就形成一一對應的關系。 通過該Class對象,就可以訪問到對應的class。所以我們可以把Class理解為一個類的標識對象,它相當於是一個類的標簽(銘牌)。拿到一個Class,我們就可以找到對應的類(class)。

二、獲取Class對象的方法

在Java中我們可以使用三個方法拿到Class對象。其中兩種是針對已經在家的類對象,去獲得他對應的Class對象。剩余一種利用到了反射,根據提供的類名去尋找對應的class文件,進而找到Class對象。

 

1 Class.forName(String className)//className表示完整的名稱,包括該類的包名。如果無法找到,該方法會拋出一個 
2 Person.class //Person代表的是一個類,class字段是其默認的屬性 
3 person.getClass() //getClass是Obj類的一個實例方法,所有的類都有該方法,包括Class類 

 

三、 從Class中可以獲取到的信息

系統可以通過Class對象,找到該對象對應的class.而Class對象包含了class的基本詳細信息。這些信息可以分為以下四個方面: 1、獲取到class所包含的構造器。 2、獲取到class所包含的方法。 3、獲取到class所包含的成員變量。 4、獲取到class所包含的Annotation。 ps 很多小伙伴可能對Annotation不太熟悉,這里簡單說下:Annotation翻譯為注解,本身也是一個類,可以用來保存類的描述信息。 有興趣的可以參考下面這篇文章:

http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html

四、在工作中Class類的使用用途

在這里我總結了一下曾經遇到的使用情況,將其分為;兩方面,如果有遺漏,大家可以補充。

1、對對象類型的使用和校驗

有些時候,我們需要對傳入對象的類型進行校驗,判斷傳入的對象是否為我們需要的類型。 

if(para.getClass==Person.class)//如果這里使用 instance關鍵字,則可能會受到Person類繼承關系的干擾,導致無法進行正確的判斷。 

 

2、反射

<1>用字符串定義需要加載的類名,然后等到需要時候再加載。

  這樣做有三個用途:

  (1)有時候並不知道此處需要加載的類型,需要在運行時才可能知道需要加載哪個class,譬如在運行的過程中,根據用戶的手動設置,動態的選擇接下來要加載的類。  

  (2)在編譯時已經知道需要加載的類名,但是尚無需要加載的.class文件,需要在運行時,通過用戶上傳,或者后台到指定地址下載class文件。   插件化開發的實現就是使用這樣一個原理。舉兩個例子:

      (α)用戶在使用過濾時,需要自己來定義一套復雜的過濾機制,這時可能就無法通過界面簡單的設置一下需要過濾的內容。可以由用戶手動的上傳自己的過濾算法的jar包,然后由后台動態的加載,使用該算法。

      (β)亦或者有時候在工作環境中,對於皮膚顯示有一套默認的顯示效果,同時也支持用戶自己上傳需要顯示效果的jar包。后台拿到用戶上傳的jar包后,反射出需要用到的特效算法,形成動態的交互。  

  (3)縮短編譯時間,加快啟動軟件的速度(包括client 和server)  

  在啟動時,包含main方法的類被加載,同時它會加載(防盜連接:本文首發自http://www.cnblogs.com/jilodream/ )所自己需要的類。這些類再一次加載自己所需要的類。形成遞推關系。但是對一個大應用程序來說,整個的啟動(加載)過程耗費的時間,常常讓用戶無法忍受,甚至在還未加載完時就被強制關閉了。

  針對這種情況,我們就可以在main方法類中只加載一些最基本的類。諸如登錄、驗證等。當登錄驗證沒有問題之后,需要進入業務操作時,才會根據用戶的選擇,   加載用戶需要的類。從而提高軟件整體的運行效率和用戶體驗。

<2>對於工具的開發和使用

當我們開發工具或腳本時,除了使用系統公開的API外,有時還需要用到原有代碼中被私有化的一些變量和方法。這時僅僅使用繼承是不夠的,還需要反射出對象,拿到其中的變量或調用其中的方法。 比如平常使用的UT框架,有時為了測試效率,就提供了很多可以直接調用待測試類私有方法的API。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM