注解(一)- 基礎知識與運行時注解

在java和android 中,注解的運用非常廣泛,很多的類庫,第三方框架中都用到了注解。所以我們有必要來熟悉注解的相關知識。

Annotation,注解(也稱為元數據),可以為我們在代碼中添加額外的信息,我們也可以很方便的使用這些數據

當然,在代碼中添加額外信息我們最經常使用的是注釋(comment),好的注釋對于理解代碼或邏輯是非常重要的,comment通俗易懂,并且使用一些工具,注釋也可以生成專門的文檔,但是注解相比注釋,擁有更加強大的,不可替代的功能。它可以提供編譯期的一些操作,比如類型檢查,生成新的文件(包括java文件等)。

annotation是java5才引入的新特性,它通過將信息和源代碼結合在一起,可以提供一些java語言本身無法的表達的額外信息。它的相關內容可以由編譯器來測試或者驗證,當然,運行時期也可以使用注解來提供額外信息。

注解的語法其實比較簡單,除了多個@符號,其他和java本身語法一樣。java 5 在java.lang中內置了三種標準注解。(android中內置的注解更多)。

  • @Override: 表示當前方法其實是覆蓋父類的方法,如果有拼寫錯誤等,編譯器(和IDE)就可以發出錯誤提示。
  • @Deprecated:表示該類或方法不建議使用了,未來有可能被廢棄或者被移除,如果程序員使用了該類或方法,那么編譯器(和IDE)將會發出警告信息。 這里也就就是warning而已,你也可以繼續使用,不過建議還是不要使用被Deprecated的API,說不定未來哪個版本就被移除了。
  • @SuppressWarnings:給編譯器一條指令,告訴它對范圍內的某些類型的警告保持靜默。這樣編譯時就不再輸出該警告了。

看一個簡單的demo:
918357-20170422225803118-292622611.png
從截圖看到看到,eclipse中對不同類型的annotation,都做了相應的提示。

并且也可以看到同一個元素上也可以使用多個不同的注解。而SuppressWarnings注解接收的其實是一個數組,例如demo當中的 unchecked,表示是未檢查的轉換時的警告,而fallthrough則表示在switch塊中,某個case沒有使用break,而直接流向了下一條case時的警告。從編碼習慣上來講,你屏蔽了fallthrough的警告,也可以告別其他人,case沒有使用break,這是因為代碼的邏輯,而不是你忘記寫了。

自定義注解

從以上demo就可以看出,注解很有用,也方便,但是內置的注解不可能滿足我們五花八門的需求,所以此時就需要我們來自定義注解了。
java中內置了四種元注解(meta-annotation),元注解就是負責注解其他注解(這句話好繞啊),來幫助我們自定義注解的。

  • @Target : 表示該注解可以用在什么地方(使用范圍),可能的ElementType參數包括:
  • CONSTRUCTOR:構造器的聲明。
  • FIELD:域聲明(包含enum的實例)。
  • LOCAL_VARIABLE : 局部變量聲明。
  • METHOD : 方法聲明。
  • PACKAGE: 包聲明。
  • PARAMETER: 參數聲明。
  • TYPE: 類,接口(包括注解類型)或 enum類型。
  • @Retention: 表示需要在什么級別保存該注解信息(生命周期),可選的RetentionPolicy參數包括:
  • SOURCE:源碼級別,注解將被編譯器丟棄。
  • CLASS: 在編譯器生成的class文件中可用,但是會被VM丟棄。(默認的就是該級別)。
  • RUNTIME: 運行期保留該注解,所以此時可以通過反射機制來讀取注解的信息。
  • @Documented:將注解包含在javadoc中。
  • @Inherited:允許子類繼承父類的注解。

具體的關于如何定義自己的注解,幾行簡單的代碼勝過千言萬語。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {public  int id();public String description() default "no description";
}
public class PasswordUtils {@UseCase(id = 47, description = "password must contain one number")public boolean validatePassword(String password){return true;}@UseCase(id = 48)public String encryptPassword(String password){return "encryptPassword()";}@UseCase(id = 49, description = "new password can't equal previously used ones")public boolean checkouForNewPassword(){return false;}
}

通過代碼可以看出,定義一個注解和定義一個interface非常相似,只不過多了一個@符號罷了。在定義注解時,使用了一些元注解,比如@Target或者 @Retention,它們分別用來表明注解的應用范圍,生命周期情況。

而從語法的角度來看,注解的使用方法,也和public,static或void等修飾符一樣,沒什么大的差別。

而定義注解時,可以看到定義體里面包含了一些比較特殊的方法。我們也稱之為配置參數,這些方法只能是public或者default訪問權限,方法的返回值就是配置參數的類型,并且我們可以為其指定默認值,我們在分析處理注解時,程序或工具就可以利用這些值。

@UseCase由UseCase.java定義,并且其中包含了兩個配置參數id和description,并且它們都是有類型的,配置參數可以使用的類型如下:

  • 所有的基本類型。(int,float,boolean等)。
  • String
  • Class
  • enum
  • Annotation
  • 以上類型的數組

關于注解元素的問題,我們還需要注意如下幾個方面。

  1. 配置參數本身還可以是一個注解,這就說明注解可以嵌套。
  2. 配置參數不能有不確定的值,也就是說,配置參數要么(在定義時)有默認值,要么(在使用時)提供相應的值。并且這個值還不能是null。所以這個是比較尷尬的地方,因此比如String類型的元素,我們不能賦值為null,那么習慣用法就是使用 "";
  3. 我們在使用注解時,采用的是 鍵值對這種語法,(id = 48),有一個快捷方式,就是注解中如果定義了名為value的配置參數,那么在使用時,如果該參數是唯一需要賦值的一個參數,那么可以不使用鍵值對,直接在括號內給出value的值就可以了。(value可以是任何合法類型的參數)。
  4. 注解可以嵌套,但是不可以繼承。而實際上,當你使用@來定義注解時,默認繼承的是java.lang.annotation.Annotation。

而對于上面我們那個 UseCase的demo,我們寫一個程序來進行處理。

public class UseCaseTracker {public static void main(String[] args) {// TODO Auto-generated method stubList<Integer> useCases = new ArrayList<Integer>();Collections.addAll(useCases, 47, 48, 49, 50);trackUseCases(useCases, PasswordUtils.class);}public static void trackUseCases(List<Integer> useCases, Class<?> cl) {for (Method m : cl.getDeclaredMethods()){// getDeclaredAnnotation  返回指定類型的 注解對象 UseCase uc = m.getDeclaredAnnotation(UseCase.class);if (uc != null){System.out.println("Found UseCase :" + uc.id() + "\t" + uc.description());useCases.remove(new Integer(uc.id()));}}System.out.println("--------------------------------------");for (int i : useCases){System.out.println("Warning : Miss use case --" + i);}} }

輸出結果為:

Found UseCase :47   password must contain one number
Found UseCase :48   no description
Found UseCase :49   new password can't equal previously used ones
--------------------------------------
Warning : Miss use case --50

通過上面代碼我們可以看出,利用反射,我們可以很好的處理注解,這其實就像處理普通類那樣。

注解的基本知識本身不復雜,那么下面我們看一個復雜點的例子。在android常用的xutils3框架中,包含了一個數據庫模塊,這個數據庫在使用時,就是通過注解來創造表名或列名的,那么下面,我們也自定義一個注解,通過注解來生成javaBean對象的創建數據庫表的語句。

//告訴注解處理器,需要生成一個數據庫表
//這個注解只能用于類,接口,enum
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {public String name() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {//這些元素都有默認值,這樣我們就不必強迫程序員必須賦值了boolean primaryKey() default false;boolean allowNull() default true;boolean unique() default false;
}
/*** @author www.yaoxiaowen.com*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {//定義了元素名為value,在符合條件時,我們使用時,可以直接在括號內輸入value的值就ok了。int value() default 0;String name() default "";Constraints constraints() default @Constraints;
}
/***  SQLInteger 和 SQLString一樣,都是要求在javabean上,根據不同的數據類型使用不同的注解*  @author www.yaoxiaowen.com*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {String name() default "";//注解嵌套Constraints constraints() default @Constraints;
}
/*** 我們的目的就是為該javabean生成一個創建表的語句* @author www.yaoxiaowem.com*/
@DBTable(name = "MEMBER")
public class Member {@SQLString(30)String firstName;@SQLString(50)String lastName;@SQLIntegerInteger age;@SQLString(value = 30,constraints = @Constraints(primaryKey = true))String handle;static int memberCount;}
public class TableCreator {public static void main(String[] args) throws Exception{StringBuilder createCommand = new StringBuilder();String className = "test.annotation.database.Member";Class<?> cl = Class.forName(className);DBTable dbTable = cl.getAnnotation(DBTable.class);String tableName = dbTable.name();//如果名字為空,就使用類名if (tableName.length() < 1){tableName = cl.getName().toUpperCase();}createCommand.append("CREATE TABLE " + tableName + "(");List<String> columnDefs = new ArrayList<String>();for (Field field : cl.getDeclaredFields()){String columnName = null;Annotation[] anns = field.getDeclaredAnnotations();if (anns.length < 1){continue;}//這里的寫法之所以簡單,因為我們每個Field上面最多只有一個 注解if (anns[0] instanceof SQLInteger){SQLInteger sInt = (SQLInteger)anns[0];//沒有名字的話,我們就使用Field的名字來做為 列名if (sInt.name().length() < 1){columnName = field.getName();}else {columnName = sInt.name();}columnDefs.add(columnName + " INT " + getConstraints(sInt.constraints()));}if (anns[0] instanceof SQLString){SQLString sString = (SQLString)anns[0];if (sString.name().length() < 1){columnName = field.getName();}else {columnName = sString.name();}columnDefs.add(columnName + " VARCHAR(" + sString.value() + ")" + getConstraints(sString.constraints()));}}for (String columnDef : columnDefs){createCommand.append("\n\t" + columnDef + ",");}//移除最后的一個逗號String tableCreate = createCommand.substring(0, createCommand.length()-1) + ");";System.out.println("TABLE Creation SQL for " + className + "  is: \n" + tableCreate);}//解析出 Constraints 注解的內容private static String getConstraints(Constraints con){String constraints = "";if (!con.allowNull()){constraints += " Not Null";}if (con.primaryKey()){constraints += " PRIMARY KEY ";}if (con.unique()){constraints += " NNIQUE ";}return constraints;}
}

輸出結果為:

TABLE Creation SQL for test.annotation.database.Member  is: 
CREATE TABLE MEMBER(firstName VARCHAR(30),lastName VARCHAR(50),age INT ,handle VARCHAR(30) PRIMARY KEY );

這個demo雖然沒有實際的意義,但是仔細分析該demo對于我們理解注解還是比較有幫助的。

在java 5當中引入 annotation時,java也引入了注解處理工具Annotation Processing Tool (apt),apt是一個可以在編譯時使用的命令行工具,但是它是Oracle提供的私有實現,所以該工具在java 8中被移除了,而在java6中,通過 JSR 269 annotation processing facility來規范了自定義注解處理器的這一功能。也有了新的API(javax.annotation.processing),而關于這些內容,我們下一個篇文章再進行介紹。


作者: www.yaoxiaowen.com

github: https://github.com/yaowen369

歡迎對于本人的博客內容批評指點,如果問題,可評論或郵件(yaowen369@gmail.com)聯系

歡迎轉載,轉載請注明出處.謝謝

轉載于:https://www.cnblogs.com/yaoxiaowen/p/6750192.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/455908.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/455908.shtml
英文地址,請注明出處:http://en.pswp.cn/news/455908.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Python基礎入門系列】第02天:Python 基礎語法

Python 語言與 Perl&#xff0c;C 和 Java 等語言有許多相似之處。但是&#xff0c;也存在一些差異。在本章中我們將來學習 Python 的基礎語法&#xff0c;讓你快速學會Python 編程。 開始你的第一個 Python 程序 Python 標識符 在 Python 里&#xff0c;標識符由字母、數字、…

如何理解操作系統的不確定性_溫度最低-273度,最高卻能有1.4億億億億度,如何定義的?...

地球繞太陽公轉&#xff0c;太陽直射點在南北回歸線之間往復的移動&#xff0c;也讓地球上出現了一年四季的變化冷熱交替&#xff0c;對于溫度我們有最直觀的感受冷暖自知&#xff0c;但僅限于很小的溫度范圍&#xff0c;在中國東北地區冬天最低溫度可以低于零下30攝氏度&#…

H.264中POC類型之探討

有 B 圖像的場合。POC 表示的是圖像顯示順序。由于POC對于參考序列的初始化,重排序及標記關系重大,所以做了如下的分析&#xff0c;以下討論情況是針對幀編碼。 pic_order_cnt_type0的時候&#xff1a; poc與frame_num沒有直接的關系&#xff0c;是顯式地出現在bit流中為pic_o…

不再單打獨斗?中國移動聯合多企業組建醫療數據公司

中國移動 6月20日消息&#xff0c;中國移動通信集團公司與浪潮集團有限公司作為發起方&#xff0c;共同組建中國健康醫療大數據股份有限公司&#xff0c;在中國移動總部舉行投資意向簽約儀式。 國家衛生和計劃生育委員會副主任金小桃,國務院國有資產監督管理委員會副主任徐福順…

【Python基礎入門系列】第03天:Python 變量與數據類型

這篇文章我們學習 Python 變量與數據類型 Python 變量類型 變量存儲在內存中的值。這就意味著在創建變量時會在內存中開辟一個空間。 基于變量的數據類型&#xff0c;解釋器會分配指定內存&#xff0c;并決定什么數據可以被存儲在內存中。 因此&#xff0c;變量可以指定不同…

HTML中的表單

HTML表單 表單用于搜集不同類型的用戶輸入&#xff0c;表單由不同類型的標簽組成&#xff0c;實現一個特定功能的表單區域&#xff08;比如&#xff1a;注冊&#xff09;&#xff0c; 首先應該用標簽來定義表單區域整體&#xff0c;在此標簽中再使用不同的表單控件來實現不同…

20169210 2016-2017-2《網絡攻防實踐》第八周總結

教材 一、Linux操作系統基本礦建概述 1、Linux操作系統發展與現狀 跨平臺的硬件支持&#xff1b;豐富的軟件支持&#xff1b;多用戶多任務&#xff1b;可靠的安全性&#xff1b;良好的穩定性&#xff1b;完善的網絡功能2、Linux系統結構 1&#xff09;Linux進程與線程管理機制2…

cad卸載_想重新安裝CAD提示已經安裝?不會卸載?進來教你卸載CAD

大家好&#xff0c;我是【小杰趣分享】&#xff0c;這里每天都會分享一下和電腦軟件、電腦硬件相關的文章或視頻。這一期小杰教大家怎么徹底卸載CAD&#xff01;CAD這款軟件是小杰見過最難卸載的軟件了&#xff0c;不知道你們有沒有遇到過卸載CAD想重新安裝&#xff0c;卻怎么都…

直擊3.15 安防行業如何維護消費者權益

一年一度的315維權活動讓眾多行業為之惶恐&#xff0c;安防行業發展至今&#xff0c;和各行各業一樣也同樣面臨著安防市場粗制濫造、假冒偽劣產品盛行的局面。 315今年的消費維權主題是 “網絡誠信 消費無憂”&#xff0c;中國消費者協會副秘書長董祝禮表示&#xff0c;網絡消費…

RBSP、SODB、EBSP三者的區別和聯系 SPS: sequence parameter sets

SODB&#xff1a;最原始的編碼數據&#xff0c;沒有任何附加數據 RBSP&#xff1a;在 SODB 的基礎上加了rbsp_stop_ont_bit&#xff08;bit 值為 1&#xff09;并用 0 按字節補位對齊EBSP&#xff1a;在 RBSP 的基礎上增加了防止偽起始碼字節&#xff08;0X03&#xff09; SPS:…

【Python基礎入門系列】第04天:Python 流程控制

在編程的世界中&#xff0c;流程控制是程序員運行的基礎&#xff0c;流程控制決定了程序按照什么樣的方式去執行&#xff0c;本節給大家介紹 Python 流程控制相關語法。 if 語句 if 語句表示如何發生什么樣的條件&#xff0c;執行什么樣的邏輯。 Python程序語言指定任何非0和…

UGLY NUMBERS II

不去重錯誤版 17.4.23 long isMin (long a, long b) { if (a > b) a b; return a;}long nthUglyNumber(long n) { long *l (long*)malloc(n*sizeof(long)); for (long i 0; i < n; i) l[i] 0; l[0] 1; long p2, p3, p5 0; for(long i 0; i < n - 1; i) { l[i …

excel合并兩列內容_還在為合并WPS表格(Excel)中兩列內容而犯愁?此方法簡單高效...

我們在處理WPS表格(Excel)數據時&#xff0c;時常需要將兩列甚至更多列的內容合并顯示在同一列中&#xff0c;就像這樣&#xff1a;這個時候大家是怎么解決的呢&#xff1f;路人&#xff1a;復制粘貼So easy&#xff01;小杜&#xff1a;不止兩三行啊……路人&#xff1a;復制粘…

Css基本語法及頁面引用

Css基本語法及頁面引用 CSS代碼出現在三個地方 </head><body><b style....>兄弟連</b> <!-- 1. 行內樣式,內聯樣式, 作為 style屬性值 --></body>css基本語法 css的定義方法是&#xff1a; 選擇器 { 屬性:值; 屬性:值; 屬性:值;} 選…

【Python基礎入門系列】第05天:Python函數

前面我們寫過九九乘法表&#xff0c;但如果我要七七乘法表或五五乘法表的話&#xff0c;你會看到三者代碼極其類似&#xff0c;只是循環變量不同&#xff0c;那么如何做到代碼重用&#xff0c;而不是簡單拷貝黏貼修改呢&#xff0c;其實可是使用函數完成這一功能! 先來試著看一…

【Java基礎】 JQuery的常用操作

jQuery的常用操作一、隱藏顯示對象id為test的元素的display修改成了“none”&#xff0c;即隱藏了id為test的元素&#xff1a;$(#test).css(display,none)或$(#test).style.display"none"我們經常用到的是切換一個元素的隱藏與現實&#xff0c;下面給出代碼&#xff…

store_coding_state (cs_cm)的作用

// 以下是對當前宏塊進行編碼&#xff08;這里的編碼主要目的是為了計算按當前組合方式編碼// 生成碼流的大小&#xff0c;因此編碼前要保存當前編碼狀態&#xff0c;編碼完成后必須恢復現場&#xff09;// S T O R E C O D I N G S T A T E //-----------------------…

第十五屆北京師范大學程序設計競賽決賽(網絡同步賽) B lca水 D 思維,找規律...

第十五屆北京師范大學程序設計競賽決賽&#xff08;網絡同步賽&#xff09; B. Borrow Classroom 題意&#xff1a;一棵樹&#xff0c;點 1為根&#xff0c;一個人從點 b到 點 c再到點 1&#xff0c;第二個人從點 a出發&#xff0c;問第二個人能否截住第一個人。 tags&#xff…

macbook所有型號大全_蘋果筆記本型號大全

很多朋友在選購蘋果筆記本也就是MacBook的時候都會考慮究竟買哪一個系列會比較好&#xff0c;下面就為大家介紹一下蘋果筆記本型號大全都有什么&#xff0c;希望以下的介紹能夠幫助到您。蘋果筆記本型號大全目前蘋果筆記本有以下的幾個主要的型號&#xff1a;1、MacBook Air是目…

【Python基礎入門系列】第06天:Python 模塊和包

在計算機程序的開發過程中&#xff0c;隨著程序代碼越寫越多&#xff0c;在一個文件里代碼就會越來越長&#xff0c;越來越不容易維護。 為了編寫可維護的代碼&#xff0c;我們把很多函數分組&#xff0c;分別放到不同的文件里&#xff0c;這樣&#xff0c;每個文件包含的代碼…