java零基礎入門-高級特性篇(十四) 類載入與反射 1

初學者有時候在做練習的時候,可能會碰到一個十分詭異的問題。今天老師講到了String字元串,好開心,打開eclipse開始寫代碼,然後一頓操作,只見雙手在鍵盤上飛馳,行雲流水般寫出如下代碼,嘴角露出一絲微笑,String也不過如此嘛。

報錯

等等,咋報錯了?這是個什麼情況?哪裡有錯?為什麼連個String類型都不給我用了?為什麼!!!接下來輕則懷疑自己智商,重則自暴自棄,從此放棄走上碼農之路。

其實大家仔細一看,哦~原來是類的名字有問題啊,怎麼能定義一個叫String的類呢,這樣寫肯定報錯呀。但請各位再仔細想一想,為什麼就不能定義一個叫String的類呢?恐怕能說出其中原因的同學並不多。

類載入器與雙親委派模型

首先來回憶一下前面的知識。編寫完java文件後,jvm是不能直接運行java文件的,首先要將java文件編譯成class文件以後,jvm再把class文件載入到內存中,創建一個Class對象,這時候才可以使用這個類。載入這個動作,就是由類載入器完成的。在jvm中有三個內置類載入器,Bootstrap ClassLoader,Extension ClassLoader,Application ClassLoader。

Bootstrap ClassLoader:啟動類載入器,用於載入java核心庫jre/lib/rt.jar

Extension ClassLoader:擴展類載入器,用於載入java擴展庫jre/ext/*.jar

Application ClassLoader:應用程序類載入器,自己寫的代碼都是使用這個載入器來載入的

先來看看這幾個載入器的工作流程

工作過程

這個類的載入過程有一個更加專業的名字,叫做雙親委派模型。這個模型名字看名字不好理解,來想像一下下面這個場景。小明一家人一起吃飯,看到大雞腿就想吃,但是中華文明優良傳統告訴他,得先問問爸爸,這個大雞腿我能吃嗎?然後爸爸又看看爺爺,問爺爺要吃嗎?爺爺說還是給小明吃吧,這時候爸爸再告訴小明,你可以吃了。爸爸和爺爺是雙親,能不能吃要先看上面,如果恰好爺爺也想吃,小明也只能幹瞪眼了。

再來看這個雙親委派模型,在載入類的時候,應用程序類載入器不會直接去載入它,它先要問它的父類載入器,它的父類載入器再去問爺類載入器,爺類載入器一看,不該我管,丟給父類,父類一看,也不是我管,最後丟回來給應用程序類載入器。

再來看看上面那個String的例子,為什麼會報錯。java本身有一個String的類型,String在哪?

java.lang.String

注意String最上面的jar包,Bootstrap ClassLoader的管轄範圍就是這個jar包,現在知道程序報錯的原因了沒?根據類的載入流程,我們希望得到的String是應該被啟動類載入器載入的String,如果String的類路徑是java.lang.String,就是我們想要的String。但是上個例子裡面,我們自定義了一個String,它的類路徑不是java.lang.String,所以他不會被啟動類載入器載入,而是最後被應用程序類載入器載入,所以它並不是我們想要的那個String,不是那個可以用字元串賦值的類型String,這時候將一個字元串賦值給這個「String」,在編譯的時候就會出錯,因為我們自定義的String不具備這個接收字元串賦值的功能。

Class類型

上面說過,jvm把class文件載入到內存中時,會創建一個Class對象。這個Class對象是什麼呢?類不是用class定義的么,怎麼還有一個Class?別暈,現在就來詳細說說Class是幹什麼的。

先從Class存在的意義來說。類是一類事物的描述,類包含了這類事物的信息,比如車這個類,包含了車的類型,用途,行為信息,可以在路上跑。魚這個類,包含了魚的種類,名稱,行為信息,可以在水裡游。

類的屬性和行為

從上圖可以看到,用屬性和行為可以描述一類事物。那麼「類」這個事物,是否也可以被描述呢?答案是肯定的,Class類就是用來描述類的信息的。Class也是一種類型,它專門用來描述類的特徵。

類類型

車這個類可以用名稱,類型,行為來描述。類這個類可以用屬性,行為來描述,所有的類都可以有屬性,都可以有行為。可以將Class類看做是普通類型的更高層抽象,用來描述普通類如何構成,包含內容等信息。

再從類的載入和對象創建來說。每寫完一個類文件,首先會被編譯成.class文件,然後在運行時,這個.class文件會被載入到jvm中,如果是第一次載入這個類,那麼會同時生成這個類對應的Class對象。當使用new關鍵字創建類的對象的時候,會首先去這個類對應的Class對象獲取該類的信息,然後創建對象,所以可以將Class對象看做是類的模板,同一個類的對象創建都使用這個模板。類創建的對象可以有很多,但是模板只有一份,也就是說每個類對應的Class對象只有一個。

關係

java文件被編譯載入後創建Class對象,當這個java文件的類需要創建對象的時候,也就是使用new關鍵字創建對象的時候,會去獲取那個已經被創建好的Class對象中的信息。這裡要注意一個重點,獲取Class對象信息的時候是運行時,只有在運行時才能通過Class獲取類的信息。

獲取Class對象

既然Class對象包含這麼多信息,那麼在程序中如何獲取Class對象?獲取Class的方法有三種:

1.Class.forName("類名"); 通過類名字元串獲取Class對象。

2.通過類的對象調用getClass() 獲取該類型的Class對象

3.通過類型直接獲取Class對象

獲取Class對象

先看第一種方法,通過類型的全限定名獲取Class對象。通過這種方式可以直接使用一個字元串來獲取Class對象,這種方式使用起來比較靈活,當需要對類型進行配置的時候,可以將對象的全限定名寫入到配置文件中,這樣就可以在不修改代碼的情況下,通過修改配置的方式來改變創建對象的類型。需要注意的是,通過Class.forName()方法獲取的對象是無法確定類型的,所以使用無限制泛型通配符。

第二種方法,是通過對象的實例的getClass()方法來獲取Class對象,這種方式要先創建類型的對象,再根據創建的對象來獲取Class對象。由於根據已知類型的對象獲取的Class,所以這個Class對象的類型可以是User或其子類。

第三種方法,直接根據類型獲取Class對象,這種方式最直接,連方法都不用調用,直接獲取Class對象,速度最快效率最高。一般情況使用這種方法獲取Class對象最為普遍。

最後要注意的一點是,同一個類只會有一個Class對象,上例中第二種和第三種方法獲取到Class對象後,將獲取到的對象與第一種方法獲取到的Class對象進行地址比較,結果都是true。可以說明,不管用哪種方法獲取Class對象,都只會獲取到同一個並且是該類唯一一個Class對象。

現在已經獲取到Class對象了,那麼該如何使用它呢?前面說過,Class對象有類裡面所有的信息,所以可以通過Class獲取到類裡面所有的構造器,方法,成員變數等等所有的信息。

獲取類信息

Class對象可以獲取到所有信息,上圖只是一個普通的類,如果獲取到ClassInfoDemo這個類對應的Class對象,就可以通過調用這個對象的方法獲取如上圖中包,成員變數,構造器,方法等信息。如果類還有其他信息比如註解,實現介面方法,內部類,外部類等等信息,都可以通過Class對象的對應方法獲取,可見Class是一個功能非常強大的類。

講了半天類的載入和Class對象,這些知識點有什麼用?這都是為了接下來登場的知識點-反射打好基礎,反射技術十分重要,在各大框架中被頻繁的使用,所以在學習的過程中,了解反射的意義對後續框架的學習有很大的幫助。

本文為了方便理解,隱藏了部分類載入中的細節,如需深入了解,請自行查閱相關資料。

推薦閱讀:

相关文章