Java高級特性入門:這三個(gè)你必須要了解(泛型湘今、反射和注解)

一、泛型介紹

在日常編程的過程中剪菱,泛型在這三個(gè)特性之中使用頻率是最高的摩瞎。”泛型”一詞中的泛字可以理解為泛化的意思孝常,即由具體的旗们、個(gè)別的擴(kuò)大為一般的。Oracle對泛型的官方定義是:泛型類型是通過類型參數(shù)化的泛型類或接口构灸。一言以蔽之上渴,泛型就是通過類型參數(shù)化,來解決程序的通用性設(shè)計(jì)和實(shí)現(xiàn)的若干問題。

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

1曹阔、編譯器類型檢查

例如上圖中的實(shí)例1設(shè)計(jì)了一個(gè)簡單的Box類,在其中定義了一個(gè)private的object的屬性隔披,同時(shí)定義了get()和set()兩個(gè)行為赃份,其中set()用于保存object到Box內(nèi),set()用于獲取Box中的object對象奢米。從抽象的角度看抓韩,Box類抽象了一個(gè)用于在盒子中存放物品對象和存取的行為,存取的方法接受或者返回Object類型的對象恃慧。在這個(gè)抽象的基礎(chǔ)上园蝠,可以存放除原始類型外任意類型的對象,Object類型的聲明體現(xiàn)了面向?qū)ο笾欣^承的理念痢士。

在實(shí)例2中彪薛,實(shí)現(xiàn)了不同業(yè)務(wù)場景下對Box的使用方式。其中列舉了兩種不同的業(yè)務(wù)場景怠蹂,場景一需要在Box中存放String類型的對象善延,場景二需要在Box中存放Integer類型的對象,這種情況下城侧,在實(shí)際開發(fā)時(shí)易遣,場景二中很有可能會(huì)錯(cuò)誤地傳入一個(gè)String對象,導(dǎo)致運(yùn)行時(shí)錯(cuò)誤的發(fā)生嫌佑,而這正是因?yàn)锽ox可以被只有傳入任意類型的對象導(dǎo)致的豆茫,這種情況在集合類操作時(shí)尤為突出。例如實(shí)例3中的情況:

首先聲明了一個(gè)List類型的boxes對象屋摇,其中存放了兩個(gè)對象揩魂,一個(gè)是String類型的“aaaaa”,另一個(gè)是Integer類型的11111炮温。在業(yè)務(wù)場景一下火脉,使用者認(rèn)為boxes中存放的所有對象都是String類型的,因此在取出第二個(gè)對象并進(jìn)行類型轉(zhuǎn)換的時(shí)候就發(fā)生了錯(cuò)誤柒啤。這種情況往往讓使用者十分迷惑倦挂,明明編譯時(shí)沒有問題,但是在運(yùn)行時(shí)卻產(chǎn)生了異常担巩。也就是說方援,在這種面向?qū)ο蟮某橄筮^程中,無法通過編譯來驗(yàn)證類型該如何進(jìn)行使用涛癌。

那么泛型是如何解決這類問題的呢肯骇?

Oracle意識到了上述的問題窥浪,在引入泛型之后,通過將代碼中的“public class Box”更改為“public class Box<T>”來創(chuàng)建泛型類型的聲明笛丙,而這個(gè)聲明的背后實(shí)質(zhì)上是引入了可以在類中任何地方使用的類型變量T。如實(shí)例4中所示:可以看到假颇,除了新增的泛型類型聲明<T>外胚鸯,所有在原來代碼中出現(xiàn)的Object都被類型變量T所替換。

乍一看類型變量這個(gè)詞笨鸡,感覺有點(diǎn)晦澀難懂姜钳,但其實(shí)如果仔細(xì)思量一番會(huì)發(fā)現(xiàn)它其實(shí)并不難理解,上面的實(shí)例4可以理解為“在使用泛型時(shí)形耗,可以將類型參數(shù)T傳遞給Box類型本身”哥桥,結(jié)合Oracle給出的官方定義“泛型的本質(zhì)是類型參數(shù)化”會(huì)有更深的理解。

在實(shí)例5中激涤,在對象聲明和初始化的時(shí)候拟糕,都指定了類型參數(shù)T,在場景一種倦踢,T為String送滞;在場景二中,T為Integer辱挥。這樣犁嗅,在場景二中向IntegerBox中傳入String類型的數(shù)據(jù)“aaaaa”時(shí),程序會(huì)報(bào)錯(cuò)晤碘。實(shí)例6中的泛型集合對象的操作也與之類似褂微,在聲明了一個(gè)List<String>的boxes對象之后,如果向boxes中傳入Integer對象11111园爷,程序會(huì)報(bào)錯(cuò)宠蚂。

可以看到,通過對于泛型的使用腮介,之前的多業(yè)務(wù)場景中的問題都得到了解決肥矢,因?yàn)楝F(xiàn)在在編譯階段就可以解決之前類型不匹配的問題,而不用等到運(yùn)行時(shí)才暴露問題叠洗,只要合理使用泛型甘改,就能在很大程度上規(guī)避此類風(fēng)險(xiǎn)。對于泛型的使用灭抑,這種參數(shù)化類型的作用表面上看是聲明十艾,背后其實(shí)是約定。

2腾节、強(qiáng)制類型轉(zhuǎn)換

再回顧一下實(shí)例3忘嫉,在List類型的boxes對象中存放了兩個(gè)對象荤牍,分別是String類型的“aaaaa”和Integer類型的11111。其中存在一個(gè)問題庆冕,在對于boxes的聲明中康吵,使用者不知道boxes的list中到底應(yīng)該存放什么類型的對象,而編譯器也不知道集合存放的數(shù)據(jù)類型访递,只能通過實(shí)際的業(yè)務(wù)場景來決定這個(gè)box是什么類型晦嵌,采用將Object強(qiáng)制轉(zhuǎn)換成String的方式,來達(dá)到業(yè)務(wù)要求的效果拷姿。

在使用泛型之后惭载,解決了這種場景下必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換的問題。如實(shí)例7中响巢,通過泛型聲明描滔,指定集合內(nèi)元素的類型參數(shù)為String類型,這樣編譯器就直接知曉了元素的類型踪古,而無需依靠實(shí)際的業(yè)務(wù)邏輯進(jìn)行轉(zhuǎn)換含长,從而解決了這類類型強(qiáng)制轉(zhuǎn)換的問題。

3灾炭、可讀性和靈活性

泛型除了能進(jìn)行編譯器類型檢查和規(guī)避類型強(qiáng)制轉(zhuǎn)換外茎芋,還能有效地提高代碼的可讀性。對于實(shí)例3蜈出,如果不使用泛型田弥,當(dāng)一個(gè)不清楚業(yè)務(wù)場景的人在對集合進(jìn)行操作時(shí),無法知道list中存儲(chǔ)的是什么類型的對象铡原,如果使用了泛型偷厦,就能夠通過其類型參數(shù)判斷出當(dāng)前的業(yè)務(wù)場景,也增加了代碼的可讀性燕刻,同時(shí)也可以大膽地在抽象繼承的基礎(chǔ)上進(jìn)行開發(fā)了只泼。

泛型使用上的靈活性體現(xiàn)在很多方面,因?yàn)樗旧韺?shí)質(zhì)上就是對于繼承在使用上的一種增強(qiáng)卵洗。因?yàn)榉盒驮诰唧w工作時(shí)请唱,當(dāng)編譯器在編譯源碼的時(shí)候,首先要進(jìn)行泛型類型參數(shù)的檢查过蹂,檢查出類型不匹配等問題十绑,然后進(jìn)行類型擦除并同時(shí)在類型參數(shù)出現(xiàn)的位置插入強(qiáng)制轉(zhuǎn)換指令,從而實(shí)現(xiàn)泛型酷勺。

除了上述的基礎(chǔ)用法之外本橙,泛型還有幾種特殊的高階用法:

通配符的設(shè)計(jì)存在一定的場景,例如在使用泛型后脆诉,首先聲明了一個(gè)Animal的類甚亭,而后聲明了一個(gè)繼承自Animal類的Cat類贷币,顯然Cat類是Animal類的子類,但是List<Cat>卻不是List<Animal>的子類型亏狰,而在程序中往往需要表達(dá)這樣的邏輯關(guān)系役纹。為了解決這種類似的場景,在泛型的參數(shù)類型的基礎(chǔ)上新增了通配符的用法暇唾,具體來說有三種用法:<? extends T>字管、<? super T>、<?>信不。其中前兩者被稱為限定通配符,<?>被稱為非限定通配符亡呵。

1抽活、<? extends T> 上界通配符

上界通配符顧名思義,<? extends T>表示的是類型的上界(包含自身)锰什,因此通配的參數(shù)化類型可能是T或T的子類下硕。正因?yàn)闊o法確定具體的類型是什么,add方法受限(可以添加null汁胆,因?yàn)閚ull表示任何類型)梭姓,但可以從列表中獲取元素后賦值給父類型。如上圖中的第一個(gè)例子嫩码,第三個(gè)add()操作會(huì)受限誉尖,原因在于List<Animal>和List<Cat>是List<? extends Animal>的子類型。

2铸题、<? super T> 下界通配符

下界通配符<? super T>表示的是參數(shù)化類型是T的超類型(包含自身)铡恕,層層至上,直至Object丢间,編譯器無從判斷get()返回的對象的類型是什么探熔,因此get()方法受限。但是可以進(jìn)行add()方法烘挫,add()方法可以添加T類型和T類型的子類型诀艰,如第二個(gè)例子中首先添加了一個(gè)Cat類型對象,然后添加了兩個(gè)Cat子類類型的對象饮六,這種方法是可行的其垄,但是如果添加一個(gè)Animal類型的對象,顯然將繼承的關(guān)系弄反了喜滨,是不可行的捉捅。

3、<?> 無界通配符

在理解了上界通配符和下界通配符之后虽风,其實(shí)也自然而然的理解了無界通配符棒口。無界通配符用<?>表示寄月,?代表了任何的一種類型,能代表任何一種類型的只有null(Object本身也算是一種類型无牵,但卻不能代表任何一種類型漾肮,所以List<Object>和List<null>的含義是不同的,前者類型是Object茎毁,也就是繼承樹的最上層克懊,而后者的類型完全是未知的)。

二七蜘、反射機(jī)制

反射是Java語言本身具備的一個(gè)重要的動(dòng)態(tài)機(jī)制谭溉。用一句話來解釋反射的定義:自控制,自描述橡卤。即通過反射可以動(dòng)態(tài)的獲取類扮念、屬性、方法的信息碧库,也能構(gòu)造對象并控制對象的屬性和行為柜与。

上圖中有一個(gè)Apple類,它有兩個(gè)構(gòu)造器嵌灰、一個(gè)屬性和get()弄匕、set()兩個(gè)行為。在左側(cè)的“自描述”中主要是嘗試在動(dòng)態(tài)的過程中借助反射獲取Apple類的構(gòu)造器信息和對應(yīng)的參數(shù)個(gè)數(shù)沽瞭、類的屬性信息和類的方法信息迁匠。其中有一個(gè)Class類型,它可以產(chǎn)生Class對象被ClassLoader加載秕脓,從而在jvm中實(shí)現(xiàn)對它的調(diào)用柒瓣。在這段程序中,打印了一些類的信息吠架、類的屬性信息和類的方法信息芙贫。在右側(cè)的“自控制”的代碼中,實(shí)現(xiàn)了在運(yùn)行的過程中創(chuàng)建了一些對象并觸發(fā)這個(gè)對象的一些行為傍药,最后還嘗試對對象的屬性進(jìn)行賦值磺平。反射的基本使用方法較為簡單,但是這種機(jī)制卻增強(qiáng)了Java語言的靈活性拐辽。

如上圖所示拣挪,非反射的Java類的大致運(yùn)行流程是:編寫源文件Apple.java,然后編譯器將其編譯成字節(jié)碼文件Apple.class俱诸,最后加載到j(luò)vm中并運(yùn)行菠劝。而采用反射的方式時(shí),編譯器一開始對其類型(編譯類型和動(dòng)態(tài)類型)是一無所知的睁搭,只有在運(yùn)行過后赶诊,編譯器才知道其真正的類型笼平。

反射的優(yōu)勢主要在于兩點(diǎn):

在一些場景中,這種“未知類型”實(shí)際上大大增強(qiáng)了程序運(yùn)行時(shí)的靈活性舔痪,但是其性能會(huì)有一些損耗寓调;

對于對象的類型可以在運(yùn)行時(shí)判斷,這樣的特性實(shí)質(zhì)上是對多態(tài)極大地增強(qiáng)锄码,進(jìn)一步地將上層的抽象與下層的具體實(shí)現(xiàn)進(jìn)行解耦夺英。

這兩點(diǎn)在JDBC Driver中體現(xiàn)的非常明顯,例如上圖中的實(shí)例中滋捶,JDBC的驅(qū)動(dòng)加載方式是通過反射機(jī)制實(shí)現(xiàn)的痛悯,從而保證運(yùn)行時(shí)可以動(dòng)態(tài)選擇要加載的驅(qū)動(dòng)程序,程序靈活性大大增強(qiáng)重窟。另外灸蟆,JDBC只是設(shè)計(jì)了驅(qū)動(dòng)需要實(shí)現(xiàn)的接口,并不關(guān)心驅(qū)動(dòng)廠商的個(gè)數(shù)和實(shí)現(xiàn)方式亲族,只要安裝統(tǒng)一的規(guī)范即可,至于類型的判斷和具體方法的觸發(fā)可缚,交給運(yùn)行期動(dòng)態(tài)判斷即可霎迫,這種反射機(jī)制的使用淋漓盡致的體現(xiàn)了多態(tài),并且降低了類與類之間的耦合度帘靡。

三知给、注解的使用

注解是在1.5版本引入的,現(xiàn)在已經(jīng)成為日常程序開發(fā)中非常重要的一部分描姚。注解是一種元數(shù)據(jù)涩赢,本身沒有任何作用,如果要有轩勘,必須依附在具體的對象上筒扒,在日常使用中最常見的兩個(gè)注解是@Override和@Deprecated。

先不考慮注解具體的概念绊寻、用法和如何工作等問題花墩,注解與“標(biāo)簽”的概念十分相似,@Override可以理解為在方法上添加了一個(gè)標(biāo)簽澄步,其代表的就是“這是一個(gè)繼承關(guān)系中冰蘑,子類已經(jīng)重寫的方法〈甯祝”更進(jìn)一步理解祠肥,這個(gè)標(biāo)簽在某個(gè)方法上加上之后,如果父類中沒有該方法梯皿,那么在編譯的時(shí)候就會(huì)報(bào)錯(cuò)仇箱,而且可以解決在繼承場景下一些不留心將方法名拼錯(cuò)的情況县恕,同時(shí)增強(qiáng)了一些程序的可讀性。

如上圖所示工碾,同樣以@Override為例弱睦,對注解進(jìn)行進(jìn)一步的提取和抽象。具體抽象出了四個(gè)方面:首先在作用域方面渊额,它只能作用于子類重寫的方法上况木;其次在生命周期方面,注解只是在編譯時(shí)進(jìn)行檢查旬迹,在編譯結(jié)束后便沒有了任何作用火惊;除此之外,在文檔支持方面奔垦,為例解決可讀性的問題屹耐,設(shè)計(jì)了@Documented的注解,用來表示注解的說明注釋是否包含在JavaDoc中椿猎;在層級結(jié)構(gòu)設(shè)計(jì)方面惶岭,設(shè)計(jì)了@inherited用來表示注解是否可以被子類繼承。

在上圖中定義了一個(gè)蘋果描述注解犯眠,包含了@Target按灶、@Retention、@Inherited和@Documented四個(gè)注解筐咧,表示它生命周期是程序運(yùn)行的聲明周期鸯旁、可以被子類繼承、文檔可以被包含量蕊。在設(shè)計(jì)出這個(gè)注解之后铺罢,可以將其用在前文中的Apple實(shí)例上,如圖中在類和方法上各添加了一個(gè)注解残炮,在添加完后韭赘,便可以配合反射看到注解的效果,這樣可以更好的加強(qiáng)其自描述的能力和配置的靈活性势就。

覺得此文不錯(cuò)的大佬們可以多多關(guān)注或者幫忙轉(zhuǎn)發(fā)分享一下哦辞居,感謝!5吧住M咴睢!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抱完,一起剝皮案震驚了整個(gè)濱河市贼陶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖碉怔,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烘贴,死亡現(xiàn)場離奇詭異,居然都是意外死亡撮胧,警方通過查閱死者的電腦和手機(jī)桨踪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芹啥,“玉大人锻离,你說我怎么就攤上這事∧够常” “怎么了汽纠?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長傀履。 經(jīng)常有香客問我虱朵,道長,這世上最難降的妖魔是什么钓账? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任碴犬,我火速辦了婚禮,結(jié)果婚禮上梆暮,老公的妹妹穿的比我還像新娘翅敌。我一直安慰自己,他們只是感情好惕蹄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著治专,像睡著了一般卖陵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上张峰,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天泪蔫,我揣著相機(jī)與錄音,去河邊找鬼喘批。 笑死撩荣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饶深。 我是一名探鬼主播餐曹,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼敌厘!你這毒婦竟也來了台猴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎饱狂,沒想到半個(gè)月后曹步,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡休讳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年讲婚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俊柔。...
    茶點(diǎn)故事閱讀 39,992評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筹麸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出婆咸,到底是詐尸還是另有隱情竹捉,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布尚骄,位于F島的核電站块差,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏倔丈。R本人自食惡果不足惜憨闰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望需五。 院中可真熱鬧鹉动,春花似錦、人聲如沸宏邮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜜氨。三九已至械筛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間飒炎,已是汗流浹背埋哟。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留郎汪,地道東北人赤赊。 一個(gè)月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像煞赢,于是被迫代替她去往敵國和親抛计。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容