點擊上方關注,每天學習一個java知識點
資料收集於雲棲社區
本次的分享主要圍繞以下三個方面:
一、泛型介紹
二、反射機制
三、註解的使用
一、泛型介紹
在日常編程的過程中,泛型在這三個特性之中使用頻率是最高的。”泛型”一詞中的泛字可以理解為泛化的意思,即由具體的、個別的擴大為一般的。Oracle對泛型的官方定義是:泛型類型是通過類型參數化的泛型類或接口。一言以蔽之,泛型就是通過類型參數化,來解決程序的通用性設計和實現的若干問題。
Java泛型是1.5版本後引入的特性,它主要被用於解決三類問題:
- 編譯期類型檢查
- 強制類型轉換
- 可讀性和靈活性
下面我們通過具體代碼實例來看下是如何解決這些問題的:
編譯期類型檢查
在實例2中,實現了不同業務場景下對Box的使用方式。其中列舉了兩種不同的業務場景,場景一需要在Box中存放String類型的對象,場景二需要在Box中存放Integer類型的對象,這種情況下,在實際開發時,場景二中很有可能會錯誤地傳入一個String對象,導致運行時錯誤的發生,而這正是因為Box可以被只有傳入任意類型的對象導致的,這種情況在集合類操作時尤為突出。
例如實例3中的情況:
那麼泛型是如何解決這類問題的呢?
乍一看類型變量這個詞,感覺有點晦澀難懂,但其實如果仔細思量一番會發現它其實並不難理解,上面的實例4可以理解為“在使用泛型時,可以將類型參數T傳遞給Box類型本身”,結合Oracle給出的官方定義“泛型的本質是類型參數化”會有更深的理解。
強制類型轉換
在使用泛型之後,解決了這種場景下必須進行強制類型轉換的問題。
可讀性和靈活性
泛型除了能進行編譯器類型檢查和規避類型強制轉換外,還能有效地提高代碼的可讀性。
對於上圖,如果不使用泛型,當一個不清楚業務場景的人在對集合進行操作時,無法知道list中存儲的是什麼類型的對象,如果使用了泛型,就能夠通過其類型參數判斷出當前的業務場景,也增加了代碼的可讀性,同時也可以大膽地在抽象繼承的基礎上進行開發了。
泛型使用上的靈活性體現在很多方面,因為它本身實質上就是對於繼承在使用上的一種增強。因為泛型在具體工作時,當編譯器在編譯源碼的時候,首先要進行泛型類型參數的檢查,檢查出類型不匹配等問題,然後進行類型擦除並同時在類型參數出現的位置插入強制轉換指令,從而實現泛型。
除了上述的基礎用法之外,泛型還有幾種特殊的高階用法:
限定通配符和非限定通配符
1、 extends T> 上界通配符
上界通配符顧名思義, extends T>表示的是類型的上界(包含自身),因此通配的參數化類型可能是T或T的子類。正因為無法確定具體的類型是什麼,add方法受限(可以添加null,因為null表示任何類型),但可以從列表中獲取元素後賦值給父類型。如上圖中的第一個例子,第三個add()操作會受限,原因在於List
2、 super T> 下界通配符
下界通配符 super T>表示的是參數化類型是T的超類型(包含自身),層層至上,直至Object,編譯器無從判斷get()返回的對象的類型是什麼,因此get()方法受限。但是可以進行add()方法,add()方法可以添加T類型和T類型的子類型,如第二個例子中首先添加了一個Cat類型對象,然後添加了兩個Cat子類類型的對象,這種方法是可行的,但是如果添加一個Animal類型的對象,顯然將繼承的關係弄反了,是不可行的。
3、> 無界通配符
在理解了上界通配符和下界通配符之後,其實也自然而然的理解了無界通配符。無界通配符用>表示,?代表了任何的一種類型,能代表任何一種類型的只有null(Object本身也算是一種類型,但卻不能代表任何一種類型,所以List
二、反射機制
反射是Java語言本身具備的一個重要的動態機制。用一句話來解釋反射的定義:自控制,自描述。即通過反射可以動態的獲取類、屬性、方法的信息,也能構造對象並控制對象的屬性和行為。
上圖中有一個Apple類,它有兩個構造器、一個屬性和get()、set()兩個行為。
“自描述”中主要是嘗試在動態的過程中藉助反射獲取Apple類的構造器信息和對應的參數個數、類的屬性信息和類的方法信息。其中有一個Class類型,它可以產生Class對象被ClassLoader加載,從而在jvm中實現對它的調用。在這段程序中,打印了一些類的信息、類的屬性信息和類的方法信息。
“自控制”的代碼中,實現了在運行的過程中創建了一些對象並觸發這個對象的一些行為,最後還嘗試對對象的屬性進行賦值。反射的基本使用方法較為簡單,但是這種機制卻增強了Java語言的靈活性。
反射的優勢
如上圖所示,非反射的Java類的大致運行流程是:編寫源文件Apple.java,然後編譯器將其編譯成字節碼文件Apple.class,最後加載到jvm中並運行。
而採用反射的方式時,編譯器一開始對其類型(編譯類型和動態類型)是一無所知的,只有在運行過後,編譯器才知道其真正的類型。
反射的優勢主要在於兩點:
- 在一些場景中,這種“未知類型”實際上大大增強了程序運行時的靈活性,但是其性能會有一些損耗。
- 對於對象的類型可以在運行時判斷,這樣的特性實質上是對多態極大地增強,進一步地將上層的抽象與下層的具體實現進行解耦。
這兩點在JDBC Driver中體現的非常明顯
例如上圖中的實例中,JDBC的驅動加載方式是通過反射機制實現的,從而保證運行時可以動態選擇要加載的驅動程序,程序靈活性大大增強。另外,JDBC只是設計了驅動需要實現的接口,並不關心驅動廠商的個數和實現方式,只要安裝統一的規範即可,至於類型的判斷和具體方法的觸發,交給運行期動態判斷即可,這種反射機制的使用淋漓盡致的體現了多態,並且降低了類與類之間的耦合度。
三、註解
註解是在1.5版本引入的,現在已經成為日常程序開發中非常重要的一部分。
註解是一種元數據,本身沒有任何作用,如果要有,必須依附在具體的對象上。
在日常使用中最常見的兩個註解是@Override和@Deprecated。
先不考慮註解具體的概念、用法和如何工作等問題,註解與“標籤”的概念十分相似。
@Override可以理解為在方法上添加了一個標籤,其代表的就是“這是一個繼承關係中,子類已經重寫的方法。”更進一步理解,這個標籤在某個方法上加上之後,如果父類中沒有該方法,那麼在編譯的時候就會報錯,而且可以解決在繼承場景下一些不留心將方法名拼錯的情況,同時增強了一些程序的可讀性。
註解-問題抽象
註解-基本用法
在設計出這個註解之後,可以將其用在前文中的Apple實例上,如上圖在類和方法上各添加了一個註解,在添加完後,便可以配合反射看到註解的效果。
這樣可以更好的加強其自描述的能力和配置的靈活性。
閱讀更多 java全棧技術 的文章