1. 程式人生 > >Java註解全面總結

Java註解全面總結

1. 簡介

註解在Java開發中扮演很重要的角色,特別在一些框架或開源庫中可以看到大量註解的運用,如果對註解不夠熟悉,那麼閱讀這些框架或開源庫的程式碼也是十分艱難的。本篇文章將從基本概念、常用註解及自定義註解三個方面來對註解進行一次全面總結,其實也是自己在深入學習註解過程中的一些心得,希望對想了解Java註解的學者有所幫助。

2. 基本概念

2.1 什麼是註解

官方給予的解釋是:

Annotations were introduced into Java in Java SE 5, providing a form of syntactic metadata that can be added to program constructs. Annotations can be processed at compile time, and they have no direct effect on the operation of the code.

翻譯成中文大概意思就是:註解在Java SE5就被引入到Java中,提供一種以元資料的形式新增到程式程式碼中。註解可以在編譯時存在,並且對程式碼的執行沒有直接的影響。

相信初學者看了這段話還是處於一種懵逼狀態,簡單的說就是通過註解可以標註一個類、方法、變數具有某種特殊含義,例如使用@deprecation, 作用在方法上表示該方法過時了等,這僅僅只是某一方面,通過註解借用反射可以實現非常強大的功能,下面在自定義註解的時候會講解到,如果對反射還不夠了解,建議先看看有關Java反射的使用看這一篇就夠了這篇文章。

2.2 註解分類

  • 按照執行機制來分
  1. 原始碼註解:註解只在原始碼中存在,編譯成.class檔案就不存在了。                                          
  2.  編譯時註解:註解在原始碼和.class檔案中都存在。                              
  3. 執行時註解:在執行階段還起作用,甚至會影響執行邏輯。
  • 按照來源來分
  1. JDK自帶的註解。                                                        
  2. 第三方提供的註解。                     
  3. 我們自定義的註解。

2.3 元註解

所謂元註解即給註解進行註解的註解,主要用來自定義註解。看概念依然難以理解,下面我們具體來講解。元註解主要有以下幾種型別:

元註解 說明 引數
@Target Annotation所修飾的物件範圍:Annotation可被用於 packages、types(類、介面、列舉、Annotation型別)、型別成員(方法、構造方法、成員變數、列舉值)、方法引數和本地變數(如迴圈變數、catch引數)

1.CONSTRUCTOR:用於描述構造器

2.FIELD:用於描述域

3.LOCAL_VARIABLE:用於描述區域性變數

4.METHOD:用於描述方法

5.PACKAGE:用於描述包

6.PARAMETER:用於描述引數

7.TYPE:用於描述類、介面(包括註解型別) 或enum宣告

@Retention 定義了該Annotation被保留的時間長短

1.SOURCE:在原始檔中有效(即原始檔保留)

2.CLASS:在class檔案中有效(即class保留)

3.RUNTIME:在執行時有效(即執行時保留)

@Inherited 執行子註解繼承, 也就是說某個父類添加了註解,對於其子類,該註解仍然有效,在自定義註解處會再次說明。
@Documented 生成文件資訊,平常比較少用。

3. Java常用註解

  • @Override

該註解表示的意思是覆寫,一般出現在繼承關係中,子類某個方法覆寫父類中的方法。

  • @deprecation

表示這個方法過時了,一般在父類或介面中標註,當然是可以在任何類中的方法上標註的。

  • @Suppvisewarnings

它表示忽視警告,例如當某個類中用了過時的方法,則編譯器會提示警告,要去除警告這可以新增該註解。

下面看如下Demo:

  1. class Person {

  2. public String getName() {

  3. return "name";

  4. }

  5. @Deprecated // 標註該方法已過時,其他地方呼叫會劃橫線,並給予警告

  6. public int getAge() {

  7. return 0;

  8. }

  9. }

  10. public class Student extends Person{

  11. @Override // 覆寫父類的方法

  12. public String getName() {

  13. return super.getName();

  14. }

  15. @SuppressWarnings("deprecation") // 忽視警告,如果沒有該註解,則下面的呼叫會出現警告

  16. public void printAge(){

  17. System.out.println(getAge());

  18. }

  19. }

4. 自定義註解

我們在自定義註解的時候,有以下幾點需要注意:

第一,只能用public或預設(default)這兩個訪問權修飾.例如,String value();這裡把方法設為defaul預設型別;   

第二,引數成員只能用基本型別byte,short,char,int,long,float,double,boolean八種基本資料型別和String,Enum,Class,annotations 等資料型別,以及這一些型別的陣列.例如,String value();這裡的引數成員就為String;  

第三,如果只有一個引數成員,最好把引數名稱設為"value",後加小括號。

如果是初學者,看到這幾個注意點,估計處於完全懵逼狀態,不知所云,下面用一個例項來說明,一定能讓你恍然大悟。

  1. @Target({ElementType.METHOD, ElementType.TYPE}) // 可以作用在方法(METHOD)和類、介面(包括註解型別) 或enum(TYPE)上

  2. @Retention(RetentionPolicy.RUNTIME) // 定義註解的型別;SOURCE,CLASS,RUNTIME

  3. @Inherited // 執行子註解繼承

  4. @Documented // 生成文件資訊;

  5. public @interface Description { // 使用@interface關鍵字定義註解;

  6. /**

  7. * 成員的型別是受限的,合法的型別包括基本型別及String,Class,Annotation,Enumeration

  8. * 如果註解只有一個成員,則成員名必須取名為value(),在使用時可以忽略成員名和賦值號(=)

  9. * 註解類可以沒有成員,沒有成員的註解稱為標識註解

  10. */

  11. String desc(); // 只能用public或預設(default)這兩個訪問權修飾

  12. public String author();

  13. int age() default 18; // 可以用default為成員指定一個預設值;

  14. }

這裡我們自定義了一個Description 型別註解,定義一個註解使用關鍵字@interface, 上面的註釋非常詳細,這裡我在說說這個註解的意思,該註解可以作用在方法、類和介面上,在程式執行時有效,父類添加了此註解同樣對子類也有效,同時還可以根據javadoc命令對類生成文件資訊,接下來我們定義了三個註解的成員,分別為desc、author和age,其中age設定了預設值。

至此,相信大家都對自定義註解有一個全面的瞭解了,此時相信大家還是有一個疑惑,我已經知道怎麼自定義註解了,可是這有什麼用呢?哪些地方需要用到註解?

彆著急,我們繼續。。。

我們使用定義的註解新增到建立的Person類中,如下:

  1. @Description(author = "lvjie", desc = "2016.8.13")

  2. public class Person {

  3. @Description(author = "jack", desc = "2016.04.10", age=20)

  4. public String getName() {

  5. return "name";

  6. }

  7. public int getAge() {

  8. return 0;

  9. }

  10. }

下面再實現一個測試類:

  1. public class MainTest {

  2. public static void main(String[] args) {

  3. try {

  4. Class c = Class.forName("annotation.demo1.Person");

  5. boolean isExist = c.isAnnotationPresent(Description.class);

  6. if(isExist){

  7. Description d = (Description) c.getAnnotation(Description.class); // 找到類上的註解;

  8. System.out.println(d.author()+" "+d.desc()+" "+d.age());

  9. }

  10. // 找到方法上的註解;

  11. Method[]ms = c.getMethods();

  12. for (Method method : ms) {

  13. boolean isMExit = method.isAnnotationPresent(Description.class);

  14. if(isMExit){

  15. Description md = method.getAnnotation(Description.class);

  16. System.out.println(md.author()+" "+md.desc()+" "+md.age());

  17. }

  18. }

  19. } catch (ClassNotFoundException e) {

  20. e.printStackTrace();

  21. }

  22. }

  23. }

執行之後,可以看到如下輸出:

                      lvjie   2016.8.13   18
                      jack   2016.04.10   20

這時候是否有那麼一點茅塞頓開的感覺,我們通過反射可以獲取哪些類哪些方法添加了此註解(這裡再次說明一下,對反射不理解的建議去看看有關Java反射的使用看這一篇就夠了這篇文章),到這裡我們是否可以聯想到Android中的一些開源庫例如EventBus、DBFlow等用到註解的意圖了,如果沒有明白,沒關係,接下來我們使用一個具體例子來說明註解給我們帶來的好處。

使用過DBFlow的開發者,相信大家都為DBFlow如此簡單的使用而驚歎,例如建立表只需要在JavaBean的類及欄位新增相關注解即可,其實DBFlow使用註解在編譯的時候為我們建立相關資料庫表,下面我們就通過註解來實現sql查詢語句的生成。

首先我們定義好兩個註解分別為Table和Column,具體如下:

  1. @Target(ElementType.TYPE) // 只作用於類或介面

  2. @Retention(RetentionPolicy.RUNTIME) // 執行時

  3. public @interface Table {

  4. String value();

  5. }

  6. @Target(ElementType.FIELD) // 只作用於欄位上

  7. @Retention(RetentionPolicy.RUNTIME)

  8. public @interface Column {

  9. String value();

  10. }

下面再定一個JavaBean即TUserInfo類

  1. @Table("UserInfo")

  2. public class TUserInfo {

  3. @Column("id")

  4. private int id;

  5. @Column("userName")

  6. private String userName;

  7. @Column("age")

  8. private int age;

  9. @Column("email")

  10. private String email;

  11. @Column("mobile")

  12. private String mobile;

  13. // 省略 get set 函式

  14. }

限於篇幅,此處省略相關get  和 set 函式。

再定義一個工具類,主要通過註解及結合反射來實現查詢sql語句的實現,具體如下:

  1. public class SqlUtils {

  2. public static String querySql(Object object){

  3. StringBuilder sb = new StringBuilder();

  4. // 獲得class

  5. Class<? extends Object> c = object.getClass();

  6. // 獲得table的名字

  7. boolean exists = c.isAnnotationPresent(Table.class); // 獲取該類是否具有Table註解

  8. if(!exists){

  9. return null;

  10. }

  11. Table t = c.getAnnotation(Table.class);

  12. String tableName = t.value(); // 獲取對應註解的值

  13. sb.append("select * from ").append(tableName).append(" where 1=1");

  14. // 遍歷所有欄位

  15. Field[] f = c.getDeclaredFields();

  16. for (Field field : f) {

  17. //4 處理每個欄位對應的sql

  18. // 4.1 拿到欄位名;

  19. boolean fExists = field.isAnnotationPresent(Column.class);

  20. if(!fExists){

  21. continue;

  22. }

  23. Column colum = field.getAnnotation(Column.class);

  24. String columName = colum.value();

  25. // 4.2 拿到欄位值

  26. String fieldName = field.getName();

  27. String getMethodName = "get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);

  28. Object fieldValue = null;

  29. try {

  30. Method getMethod = c.getMethod(getMethodName);

  31. fieldValue = getMethod.invoke(object);

  32. } catch (Exception e) {

  33. // TODO Auto-generated catch block

  34. e.printStackTrace();

  35. }

  36. // 4.3 拼接sql

  37. if(fieldValue==null || (fieldValue instanceof Integer && (Integer)fieldValue==0)){

  38. continue;

  39. }

  40. sb.append(" and ").append(columName);

  41. if(fieldValue instanceof String){

  42. if(((String) fieldValue).contains(",")){

  43. String[] values = ((String) fieldValue).split(",");

  44. sb.append(" in(");

  45. for (String string : values) {

  46. sb.append("'").append(string).append("'").append(",");

  47. }

  48. sb.deleteCharAt(sb.length()-1);

  49. sb.append(")");

  50. }else{

  51. sb.append("='").append(fieldValue).append("'");

  52. }

  53. }else if(fieldValue instanceof Integer){

  54. sb.append("=").append(fieldValue);

  55. }

  56. }

  57. return sb.toString();

  58. }

  59. }

下面編寫測試類:

  1. public class MainTest {

  2. public static void main(String[] args) {

  3. TUserInfo userInfo = new TUserInfo();

  4. userInfo.setAge(20);

  5. userInfo.setUserName("lvjie");

  6. userInfo.setEmail("[email protected], [email protected], [email protected]");

  7. String sql = SqlUtils.querySql(userInfo);

  8. System.out.println(sql);

  9. }

  10. }

執行,列印資料如下:

select * from UserInfo where 1=1 and userName='lvjie' and age=20 and email in('[email protected]',' [email protected]',' [email protected]')

是不是很簡單,針對這個demo,如果此時我們有增加了一個部門表,例如下:

  1. @Table("TDepartment")

  2. public class TDepartment {

  3. @Column("id")

  4. private int id;

  5. @Column("departmentName")

  6. private String departmentName;

  7. @Column("departmentLeader")

  8. private String departmentLeader;

  9. // 省略set get 函式

  10. }

  1. TDepartment department = new TDepartment();

  2. department.setId(10001);

  3. department.setDepartmentLeader("lvjie");

  4. System.out.println(SqlUtils.querySql(department));

輸出入下:

select * from TDepartment where 1=1 and id=10001 and departmentLeader='lvjie'

此時,相信大家已經對註解刮目相看了吧,盡然能做到如此通用性,結合反射的使用,簡直就是雙劍合璧。

總結

本篇文章對註解做了一個全面解析,從基本概念出發到最後的例項運用,只有在例項中才能體會註解的真諦,希望對大家有所幫助,同時也讓自己對註解的理解更加深刻。

參考文獻

相關推薦

Java註解全面總結

1. 簡介 註解在Java開發中扮演很重要的角色,特別在一些框架或開源庫中可以看到大量註解的運用,如果對註解不夠熟悉,那麼閱讀這些框架或開源庫的程式碼也是十分艱難的。本篇文章將從基本概念、常用註解及自定義註解三個方面來對註解進行一次全面總結,其實也是自己在深入學習註解過程中

java註解使用總結

2005年,sun公司推出了jdk1.5,同時推出的註解功能吸引了很多人的目光,使用註解編寫程式碼,能夠減輕java程式設計師繁瑣配置的痛苦。 使用註解可以編寫出更加易於維護,bug更少的程式碼。 註解是什麼呢?按照官方的說法,註解就是元標籤,可以新增到你的程式碼,並應用於包宣告、型別宣告、建構函式、方法、欄

【iMooc】全面解析java註解

test sql語句 字符 con public insert OS 直接 變量 在慕課上學習了一個關於java註解的課程,下面是筆記以及一些源碼。 Annotation——註解 1.JDK中的註解 JDK中包括下面三種註解: @Override:標記註解(marker a

java 註解總結

struct 文檔 nes CA 方法調用 表示 pan dep truct @Controller用於標註控制層組件 @Controller 用於標記在一個類上,使用它標記的類就是一個SpringMVC Controller 對象。分發處理器將會掃描使用了該註解的類的方

分享知識-快樂自己:全面解析 java註解實戰指南

請你在看這篇文章時,不要感到枯燥,從頭到尾一行行看,程式碼一行行讀,你一定會有所收穫的。 問:   為什麼學習註解?   學習註解有什麼好處?   學完能做什麼? 答:   1):能夠讀懂別人的程式碼,特別是框架相關的程式碼   2):讓程式設計更加簡潔,程式碼更加清晰   3):讓別人高看一

java註解(Annotation)總結

java註解(Annotation)總結 吐糟時間 註解是什麼 註解有什麼用 註解的分類 怎麼自定義註解 新增屬性 註解的使用 解析執行時(RUNTIME)註解 解析編譯時(CLASS)註解

JAVA類的全面總結

java是面向物件: 下面解釋什麼面向物件 JAVA操作都是類中操作如上圖Cat類,例項化可以在main函式中 那麼java本身就是類操作,為什麼還要學習繼承多型介面包? 官方回答:可讀性強,安全性高,程式碼簡化,維護性高,方便查詢錯誤等 下面我要從現實生活的角度

Java註解基礎概念總結

註解的概念 註解(Annotation),也叫元資料(Metadata),是Java5的新特性,JDK5引入了Metadata很容易的就能夠呼叫Annotations。註解與類、介面、列舉在同一個層次,並可以應用於包、型別、構造方法、方法、成員變數、引數、本地變數的宣告中,用來對這些元素進行說明註釋。 註

Java註解(Annotation)學習總結

註解的定義 註解通過 @interface 關鍵字進行定義。 public @interface TestAnnotation { } 它的形式跟介面很類似,不過前面多了一個 @ 符號。上面的程式碼就建立了一個名字為 TestAnnotaion 的註解。你可以簡

2018全面總結阿里巴巴Java開發手冊

其實早在多年前,Google就已經把公司內部採用的所有語言的編碼規範(其稱為 Style Guide )都開源在Github上。這份清單中包括了 C++ 、 Objective-C 、 Java 、 Python 、 R 、 Shell 、 HTML/CSS 、 JavaScript 、 A

Java反射和註解一些總結

Java 反射和註解的一些總結 反射 // There are five kinds of classes (or interfaces): // a) Top level classes // b) Nested classes (static m

Java位操作全面總結

在計算機中所有資料都是以二進位制的形式儲存的。位運算其實就是直接對在記憶體中的二進位制資料進行操作,因此處理資料的速度非常快。在實際程式設計中,如果能巧妙運用位操作,完全可以達到四兩撥千斤的效果,正因為位操作的這些優點,所以位操作在各大IT公司的筆試面試中一直是個熱點問題。 位操作基礎 基本的位操作符有與、

【框架基礎】:全面解析Java註解(一)

       Java註解概述        要了解Java註解要先知道Java的反射,反射是執行時獲取類的成員,註解也是類的成員,以此達到動態編碼的效 果,多用在框架,或者使用框架時候添加註解讓框架

Hibernate基於註解方式的各種對映全面總結

1. 使用HibernateAnnotation來做物件關係對映  1) 新增必須包:     hibernate-jpa-2.0-api-1.0.0.Final.jar  2)在實體類中新增JPA的標準註解來進行物件關係對映.註解可以新增在屬性上,也可以新增在getXxx

框架的基礎--全面解析java註解

閱讀目錄 概念 Java中的常見註解 註解的分類 自定義註解 註解的專案實戰 註解總結 java註解在java 1.5版本引入 一、概念 Java提供一種原程式中的元素關聯任何資訊和熱河元資料的途徑和方法。 二、Java中常

全面總結阿里巴巴Java開發手冊

2016年底,阿里巴巴公開了其在內部使用的Java程式設計規範。隨後進行了幾次版本修訂,筆者當時看到的版本為v1.0.2版。下載地址可以在其官方社群—— 雲棲社群 找到。 筆者作為一名有數年工作經驗的Java程式設計師,仔細研讀了這份手冊,覺得是一份不可多得的好材料。正

java web 解決中文亂碼問題(全面總結

  在開發一個完整的web專案時,總是會遇到各種各樣的中文亂碼問題,例如頁面顯示亂碼,表單提交亂碼,資料庫儲存亂碼等          等,雖然目前也能找到各種各樣的解決方案,但是大部分都沒有總結全面。(我也遇到了中文亂碼問題 這是我抄襲來的 如果原作者需要 請通知我刪

Java復習總結——註解

tor 創建 onf ice 不知道 urn package 成員 class 註解 概念及作用 概念 註解即元數據,就是源代碼的元數據 註解在代碼中添加信息提供了一種形式化的方法,可以在後續中更方便的 使用這些數據 Annotation是一種應用於類、方法、參數、變

seall的Java IO流總結

如果 com read 對稱 可能 對象實例化 它的 通過 兩個 Java流類圖結構: 流的概念和作用 流是一組有順序的,有起點和終點的字節集合,是對數據傳輸的總稱或抽象。即數據在兩設備間的傳輸稱為流,流的本質是數據傳輸,根據數據傳輸特性將流抽象為各種類,方便更直觀

java線程總結--synchronized關鍵字,原理以及相關的鎖

public 關鍵字 多線程 java 文章 在多線程編程中,synchronized關鍵字非常常見,當我們需要進行“同步”操作時,我們很多時候需要該該關鍵字對代碼塊或者方法進行鎖定。被synchronized鎖定的代碼塊,只能同時有一條線程訪問該代碼塊。上面是很多人的認識,當然也是我之前