「每日分享」Java高級特性入門——泛型、反射和註解

點擊上方關注,每天學習一個java知識點

資料收集於雲棲社區

本次的分享主要圍繞以下三個方面:

一、泛型介紹

二、反射機制

三、註解的使用


一、泛型介紹

在日常編程的過程中,泛型在這三個特性之中使用頻率是最高的。”泛型”一詞中的泛字可以理解為泛化的意思,即由具體的、個別的擴大為一般的。Oracle對泛型的官方定義是:泛型類型是通過類型參數化的泛型類或接口。一言以蔽之,泛型就是通過類型參數化,來解決程序的通用性設計和實現的若干問題。

Java泛型是1.5版本後引入的特性,它主要被用於解決三類問題:

  • 編譯期類型檢查
  • 強制類型轉換
  • 可讀性和靈活性

下面我們通過具體代碼實例來看下是如何解決這些問題的:

編譯期類型檢查

「每日分享」Java高級特性入門——泛型、反射和註解

「每日分享」Java高級特性入門——泛型、反射和註解

在實例2中,實現了不同業務場景下對Box的使用方式。其中列舉了兩種不同的業務場景,場景一需要在Box中存放String類型的對象,場景二需要在Box中存放Integer類型的對象,這種情況下,在實際開發時,場景二中很有可能會錯誤地傳入一個String對象,導致運行時錯誤的發生,而這正是因為Box可以被只有傳入任意類型的對象導致的,這種情況在集合類操作時尤為突出。

例如實例3中的情況:

「每日分享」Java高級特性入門——泛型、反射和註解

那麼泛型是如何解決這類問題的呢?

「每日分享」Java高級特性入門——泛型、反射和註解

乍一看類型變量這個詞,感覺有點晦澀難懂,但其實如果仔細思量一番會發現它其實並不難理解,上面的實例4可以理解為“在使用泛型時,可以將類型參數T傳遞給Box類型本身”,結合Oracle給出的官方定義“泛型的本質是類型參數化”會有更深的理解。

「每日分享」Java高級特性入門——泛型、反射和註解

「每日分享」Java高級特性入門——泛型、反射和註解

強制類型轉換

「每日分享」Java高級特性入門——泛型、反射和註解

「每日分享」Java高級特性入門——泛型、反射和註解

「每日分享」Java高級特性入門——泛型、反射和註解

在使用泛型之後,解決了這種場景下必須進行強制類型轉換的問題。

「每日分享」Java高級特性入門——泛型、反射和註解

可讀性和靈活性

泛型除了能進行編譯器類型檢查和規避類型強制轉換外,還能有效地提高代碼的可讀性。

「每日分享」Java高級特性入門——泛型、反射和註解

對於上圖,如果不使用泛型,當一個不清楚業務場景的人在對集合進行操作時,無法知道list中存儲的是什麼類型的對象,如果使用了泛型,就能夠通過其類型參數判斷出當前的業務場景,也增加了代碼的可讀性,同時也可以大膽地在抽象繼承的基礎上進行開發了。

「每日分享」Java高級特性入門——泛型、反射和註解

泛型使用上的靈活性體現在很多方面,因為它本身實質上就是對於繼承在使用上的一種增強。因為泛型在具體工作時,當編譯器在編譯源碼的時候,首先要進行泛型類型參數的檢查,檢查出類型不匹配等問題,然後進行類型擦除並同時在類型參數出現的位置插入強制轉換指令,從而實現泛型。

除了上述的基礎用法之外,泛型還有幾種特殊的高階用法:

限定通配符和非限定通配符

1、 extends T> 上界通配符

「每日分享」Java高級特性入門——泛型、反射和註解

上界通配符顧名思義, extends T>表示的是類型的上界(包含自身),因此通配的參數化類型可能是T或T的子類。正因為無法確定具體的類型是什麼,add方法受限(可以添加null,因為null表示任何類型),但可以從列表中獲取元素後賦值給父類型。如上圖中的第一個例子,第三個add()操作會受限,原因在於List

和List是List extends Animal>的子類型。

2、 super T> 下界通配符

「每日分享」Java高級特性入門——泛型、反射和註解

下界通配符 super T>表示的是參數化類型是T的超類型(包含自身),層層至上,直至Object,編譯器無從判斷get()返回的對象的類型是什麼,因此get()方法受限。但是可以進行add()方法,add()方法可以添加T類型和T類型的子類型,如第二個例子中首先添加了一個Cat類型對象,然後添加了兩個Cat子類類型的對象,這種方法是可行的,但是如果添加一個Animal類型的對象,顯然將繼承的關係弄反了,是不可行的。

3、> 無界通配符

「每日分享」Java高級特性入門——泛型、反射和註解

在理解了上界通配符和下界通配符之後,其實也自然而然的理解了無界通配符。無界通配符用>表示,?代表了任何的一種類型,能代表任何一種類型的只有null(Object本身也算是一種類型,但卻不能代表任何一種類型,所以List和List的含義是不同的,前者類型是Object,也就是繼承樹的最上層,而後者的類型完全是未知的)。

二、反射機制

反射是Java語言本身具備的一個重要的動態機制。用一句話來解釋反射的定義:自控制,自描述。即通過反射可以動態的獲取類、屬性、方法的信息,也能構造對象並控制對象的屬性和行為。

「每日分享」Java高級特性入門——泛型、反射和註解

上圖中有一個Apple類,它有兩個構造器、一個屬性和get()、set()兩個行為。

「每日分享」Java高級特性入門——泛型、反射和註解

“自描述”中主要是嘗試在動態的過程中藉助反射獲取Apple類的構造器信息和對應的參數個數、類的屬性信息和類的方法信息。其中有一個Class類型,它可以產生Class對象被ClassLoader加載,從而在jvm中實現對它的調用。在這段程序中,打印了一些類的信息、類的屬性信息和類的方法信息。

「每日分享」Java高級特性入門——泛型、反射和註解

“自控制”的代碼中,實現了在運行的過程中創建了一些對象並觸發這個對象的一些行為,最後還嘗試對對象的屬性進行賦值。反射的基本使用方法較為簡單,但是這種機制卻增強了Java語言的靈活性。

反射的優勢

「每日分享」Java高級特性入門——泛型、反射和註解

如上圖所示,非反射的Java類的大致運行流程是:編寫源文件Apple.java,然後編譯器將其編譯成字節碼文件Apple.class,最後加載到jvm中並運行。

而採用反射的方式時,編譯器一開始對其類型(編譯類型和動態類型)是一無所知的,只有在運行過後,編譯器才知道其真正的類型。

「每日分享」Java高級特性入門——泛型、反射和註解

反射的優勢主要在於兩點:

  1. 在一些場景中,這種“未知類型”實際上大大增強了程序運行時的靈活性,但是其性能會有一些損耗。
  2. 對於對象的類型可以在運行時判斷,這樣的特性實質上是對多態極大地增強,進一步地將上層的抽象與下層的具體實現進行解耦。

這兩點在JDBC Driver中體現的非常明顯

「每日分享」Java高級特性入門——泛型、反射和註解

例如上圖中的實例中,JDBC的驅動加載方式是通過反射機制實現的,從而保證運行時可以動態選擇要加載的驅動程序,程序靈活性大大增強。另外,JDBC只是設計了驅動需要實現的接口,並不關心驅動廠商的個數和實現方式,只要安裝統一的規範即可,至於類型的判斷和具體方法的觸發,交給運行期動態判斷即可,這種反射機制的使用淋漓盡致的體現了多態,並且降低了類與類之間的耦合度。

三、註解

註解是在1.5版本引入的,現在已經成為日常程序開發中非常重要的一部分。

註解是一種元數據,本身沒有任何作用,如果要有,必須依附在具體的對象上。

在日常使用中最常見的兩個註解是@Override和@Deprecated。

先不考慮註解具體的概念、用法和如何工作等問題,註解與“標籤”的概念十分相似。

@Override可以理解為在方法上添加了一個標籤,其代表的就是“這是一個繼承關係中,子類已經重寫的方法。”更進一步理解,這個標籤在某個方法上加上之後,如果父類中沒有該方法,那麼在編譯的時候就會報錯,而且可以解決在繼承場景下一些不留心將方法名拼錯的情況,同時增強了一些程序的可讀性。

註解-問題抽象

「每日分享」Java高級特性入門——泛型、反射和註解

註解-基本用法

「每日分享」Java高級特性入門——泛型、反射和註解

「每日分享」Java高級特性入門——泛型、反射和註解

在設計出這個註解之後,可以將其用在前文中的Apple實例上,如上圖在類和方法上各添加了一個註解,在添加完後,便可以配合反射看到註解的效果。

「每日分享」Java高級特性入門——泛型、反射和註解

這樣可以更好的加強其自描述的能力和配置的靈活性。


分享到:


相關文章: