Java高級特性——注解,這也許是最簡單易懂的文章了

java注解是jdk1.5以后新出的特性,對于它的應(yīng)用非常廣泛,我們首先來看一下注解的應(yīng)用,百度百科上這樣說:

我們可以看到,注解的作用有三方面:

編寫doc文檔:這個就我們很常用的 @return 以及 @author,加了這些注解以后,就可以用jdk幫我們自動生成對應(yīng)的API文檔了

編譯檢查:這個也很常見 @Override,而且功能很強(qiáng)大,我將會在以后的文章中介紹

進(jìn)行代碼分析:這是本篇文章的重點。這個和編譯檢查一樣也是一個強(qiáng)大的功能,但相比與編譯檢查由于其用到了反射,在性能上存在一些問題

后臺開發(fā)中的SSH三大框架,以及咱們安卓端的retrofit,ButterKnife,Lombok等框架和插件也是大量的用到了注解。這里我將通過手?jǐn)]一個假的ButterKnife來具體演示注解有什么用,怎么用。

我們首先來看段代碼

publicclassMainActivityextendsAppCompatActivity{@OnClick(R.id.test_btn)voidtest(){? ? test_tv.setText("恭喜您,綁定成功了!");}@FindViewByID(R.id.test_tv)TextView test_tv;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);? ? setContentView(R.layout.activity_main);? ? ButterKnife.bindView(this);}}

這是一個最基本的activity,里面有2個控件,Button和TextView

在我點擊Button后TextView的文字被改變。而我所做的僅僅是ButterKnife.bindView(this)并添加2個注解而已,這樣就實現(xiàn)了控件的綁定,省去了很多與業(yè)務(wù)無關(guān)的代碼,是不是簡潔了很多。

看了注解的功能是不是很想了解它是怎么做到的,接下來我就來看看它是什么,怎么用,怎么利用

什么是注解

官方把它叫做元數(shù)據(jù),即一種描述數(shù)據(jù)的數(shù)據(jù)。所以,可以說注解就是源代碼的元數(shù)據(jù)。用它來可以來描述、標(biāo)記我們的源代碼。

怎樣定義一個注解

以下是我上文中定義的一個 @OnClick注解

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceOnClick {intvalue()default0;}

可見和定義一個類一樣,只是將class改為 @interface,并且頂部通過幾個原注解來說明這個注解的一些重要信息,具體如下:

J2SE5.0版本在 java.lang.annotation提供了四種元注解,專門注解其他的注解:

@Documented –注解是否將包含在JavaDoc中

@Retention –什么時候使用該注解

@Target? –注解用于什么地方

@Inherited – 是否允許子類繼承該注解

@Documented–一個簡單的Annotations標(biāo)記注解,表示是否將注解信息添加在java文檔中,一般不用管。

@Retention– 定義該注解的生命周期,很重要,必須指定,以下是3種生命周期的介紹

RetentionPolicy.SOURCE – 在編譯階段丟棄。這些注解在編譯結(jié)束之后就不再

有任何意義,所以它們不會寫入字節(jié)碼。@Override, @SuppressWarnings都屬

于這類注解。

RetentionPolicy.CLASS – 在類加載的時候丟棄。在字節(jié)碼文件的處理中有用。

注解默認(rèn)使用這種方式。

RetentionPolicy.RUNTIME– 始終不會丟棄,運(yùn)行期也保留該注解,因此可以使

用反射機(jī)制讀取該注解的信息。我們自定義的注解通常使用這種方式。

@Target – 表示該注解用于什么地方。如果不明確指出,該注解可以放在任何地方。以下是一些可用的參數(shù)。需要說明的是:屬性的注解是兼容的,如果你想給7個屬性都添加注解,僅僅排除一個屬性,那么你需要在定義target包含所有的屬性。

ElementType.TYPE:用于描述類、接口或enum聲明ElementType.FIELD:用于描述實例變量ElementType.METHODElementType.PARAMETERElementType.CONSTRUCTORElementType.LOCAL_VARIABLEElementType.ANNOTATION_TYPE另一個注釋ElementType.PACKAGE用于記錄java文件的package信息

@Inherited – 定義該注釋和子類的關(guān)系

那么注解體里的內(nèi)容有該怎樣定義

Annotations只支持基本類型、String及枚舉類型。注釋中所有的屬性被定義成方法,并允許提供默認(rèn)值。

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@interfaceBook{publicenumPriority {LOW, MEDIUM, HIGH}Stringauthor()default"Yash";intprice()default20;Statusstatus()defaultStatus.NOT_STARTED;}

看看怎么用它

@Todo(priority = Todo.Priority.MEDIUM, author ="zsq", status = Todo.Status.STARTED)publicvoidincompleteMethod1(){}

通過字段名 = 的形式給字段賦值,如果沒賦值,則使用缺省值。如果注解中只有一個屬性,可以直接命名為“value”,使用時無需再標(biāo)明屬性名,例如我定義的 @OnClick注解。

好了,花了那么多精力來認(rèn)識他,該看看該怎么來利用它了

我們定義了自己的注解并將其應(yīng)用在業(yè)務(wù)邏輯的方法上?,F(xiàn)在我們需要寫一個用戶程序調(diào)用我們的注解。這里我們需要使用反射機(jī)制。如果你熟悉反射代碼,就會知道反射可以提供類名、方法和實例變量對象。所有這些對象都有g(shù)etAnnotation()這個方法用來返回注解信息。我們需要把這個對象轉(zhuǎn)換為我們自定義的注釋(使用 instanceOf()檢查之后),同時也可以調(diào)用自定義注釋里面的方法。

所有這些對象都有g(shù)etAnnotation()!

所有這些對象都有g(shù)etAnnotation()!

所有這些對象都有g(shù)etAnnotation()!

重要的API說3遍,另外用到的幾個方法也很重要,下面的代碼會演示,更多的API使用參考可以去查閱JDK文檔。

具體到我們本編文章的實例,調(diào)用注解的家伙就是我們剛剛在MainActivity里用到的 ButterKnife,我們通過設(shè)置監(jiān)聽的注解來看看它到底做了什么

publicstaticfinalvoidbindView(finalActivity activity){? ? traversalMethod(activity);? ? traversalField(activity);}

在我們調(diào)用的ButterKnife.bindView(this)中我們拿到了MainActivity的實例,并且通過反射遍歷里面所有的方法:

privatestaticvoidtraversalMethod(finalActivity activity){? ? Method[] methodArray = getObjectMethodArray(activity);for(finalMethod method:methodArray){if(isAnnotationPresent(method)){intviewID = getViewID(method);? ? ? ? ? ? setOnClickListenerForControl(activity, method, viewID);? ? ? ? }? ? }}privatestaticMethod[] getObjectMethodArray(Activity activity) {returnactivity.getClass().getMethods();}

接著判斷方法是否被我們注解:

privatestaticbooleanisAnnotationPresent(Method method){returnmethod.isAnnotationPresent(OnClick.class);}

如果是我們用注解標(biāo)注的方法則通過注解獲取注解里保存的空間ID,并且通過MainActivity的實例為其設(shè)置點擊監(jiān)聽,在監(jiān)聽內(nèi)調(diào)用被注解標(biāo)注的方法。

privatestaticintgetViewID(Method method){returnmethod.getAnnotation(OnClick.class).value();}privatestaticvoidsetOnClickListenerForControl(finalActivity activity,finalMethod method,intviewID){? ? activity.findViewById(viewID).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View view){try{? ? ? ? ? ? ? ? method.invoke(activity);? ? ? ? ? ? }catch(IllegalAccessException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }catch(InvocationTargetException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }? ? ? ? }? ? });}

大功告成!是不是很簡單

我們用反射獲取注解的方式實現(xiàn)了ButterKnife的功能,但文章開頭說過反射的存在性能上的不足。而實際上ButterKnife本身用的也不是反射,而是用的apt工具在編譯時期就可以獲取到所有的方法、字段、以及他們的注解,從而避免了使用反射,解決了性能的問題。接下來的文章我會講解本文開頭提到的第三點,也就是ButterKnife實際使用的方法,將我們自己的ButterKnife改為ButterKnife官方的實現(xiàn)方法。

1、具有1-5工作經(jīng)驗的,面對目前流行的技術(shù)不知從何下手,

需要突破技術(shù)瓶頸的。2、在公司待久了,過得很安逸,

但跳槽時面試碰壁。需要在短時間內(nèi)進(jìn)修、跳槽拿高薪的。

3、如果沒有工作經(jīng)驗,但基礎(chǔ)非常扎實,對java工作機(jī)制,

常用設(shè)計思想,常用java開發(fā)框架掌握熟練的。

4、覺得自己很牛B,一般需求都能搞定。

但是所學(xué)的知識點沒有系統(tǒng)化,很難在技術(shù)領(lǐng)域繼續(xù)突破的。

5. 群號:高級架構(gòu)群725633148備注好信息!

6.阿里Java高級大牛直播講解知識點,分享知識,

多年工作經(jīng)驗的梳理和總結(jié),帶著大家全面、

科學(xué)地建立自己的技術(shù)體系和技術(shù)認(rèn)知!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容