JAVA的註解

註解

1、註解的作用

  • 可以用於編譯器發現錯誤或者抑制異常
  • 編譯時可以利用註解生成代碼,部署時可以處理XML文件等。
  • 可以用於運行時檢查

2、基礎

一般格式如:@Entity,使用@開頭,向編譯器表示這是個註解.
註解可以包含參數:

@Author(
    name = "Benjamin Franklin",
       date = "3/27/2003"
)    
class MyClass {...}

或者:

@SupressWarnings(value = "unchecked")
void myMethod() {...}  

如果只有一個參數名爲value, 則該參數名可以忽略不寫。

@SuppressWarnings("unchecked")
void myMethod() { ... }

如果註解不包含參數,括號也可以忽略不寫,而且多個註解可以疊加。

@Author(name = "Jane Doe")
@EBook
class MyClass { ... }

也可以同時存在多個相同類型的註解:

@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }

重複註解在Java SE 8被支持. 在前面的例子中,Override和SuppressWarnings是預定義註解,詳見predefined Java annotations. 其他的如AuthorEbook爲自定義註解.

3、註解的使用場景

註解可以應用於各種聲明: 類、變量、方法,以及其他編程元素。當使用在聲明時,註解一般出現在聲明的同一行.

  • 創建類實例

    new @Interned MyObject();
    
  • 類型轉換

    myString = (@NonNull String) str;
    
  • 實現接口

    class UnmodifiableList<T> implements
        @Readonly List<@Readonly T> { ... }
    
  • 拋出異常聲明

    void monitorTemperature() throws
        @Critical TemperatureException { ... }
    

這種形式的註解被稱爲類型註解,詳見Type Annotations and Pluggable Type Systems

4、聲明註解類型##

許多註解可以在代碼中替換註釋。
一般在類開始的地方會加上一些註釋,如:

public class Generation3List extends Generation2List {

   // Author: John Doe
   // Date: 3/17/2002
   // Current revision: 6
   // Last modified: 4/12/2004
   // By: Jane Doe
   // Reviewers: Alice, Bill, Cindy

   // class code goes here

}

如果需要用註解類型的元數據替換該註釋,需要首先定義一個註解類型,如:

@interface ClassPreamble {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}

註解類型的定義與接口類型定義類似,只是需要在關鍵字interface前面加上 @ 符號。 註解類型是接口的一種形式。
定義完註解類型之後,就可以使用該註解,如:

@ClassPreamble (
   author = "John Doe",
   date = "3/17/2002",
   currentRevision = 6,
   lastModified = "4/12/2004",
   lastModifiedBy = "Jane Doe",
   // Note array notation
   reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {

// class code goes here

}

Note: 如果需要 @ClassPreamble 中的信息能出現在Javadoc生成的文檔中,需要加上 @Documented 註解.

// import this to use @Documented
import java.lang.annotation.*;

@Documented
@interface ClassPreamble {

   // Annotation element definitions

}

4、預定義註解類型

Java SE中定義了一系列註解類型。有些用於Java編譯器,有些用於其他的註解。

Java語言中使用的註解類型

在java.lang中定義的註解類型有@Deprecated, @Override 和 @SuppressWarnings.

@Deprecated @Deprecated註解表示所標記的元素是退化的並且在未來不再使用。當在程序中使用@Deprecated註解標記方法、類或者變量時,編譯器會產生警告。當一個元素退化時,也應該使用Javadoc的@deprecated標籤,如下例所示。在Javadoc和註解中都使用 @ 符號並不是一個巧合: 他們存在概念上的關聯。 同時請注意:註解使用大寫的D,Javadoc使用小寫的d開頭。

 // Javadoc comment follows
/**
 * @deprecated
 * explanation of why it was deprecated
 */
@Deprecated
static void deprecatedMethod() { }

@Override@Override註解告訴編譯器該方法會覆寫超類的方法。覆寫將在interfaces and Inheritance中討論。

 // mark method as a superclass method
 // that has been overridden
 @Override 
 int overriddenMethod() { }

當覆寫一個方法時,該註解並不是必須的,它只是幫助預防錯誤。如果用@Override標記的方法不能正確覆寫超類方法,則編譯器會報錯。

@SuppressWarnings@SuppressWarnings該註解告訴編譯器抑制特定警告的產生。 如下例所示,退化函數的使用,編譯器會產生警告,在這種情況下,註解會抑制警告的產生。

 // use a deprecated method and tell 
   // compiler not to generate a warning
   @SuppressWarnings("deprecation")
    void useDeprecatedMethod() {
    // deprecation warning
    // - suppressed
    objectOne.deprecatedMethod();
    }

每個編譯器警告都屬於一個類別。Java語言說明書列了兩個類別:deprecation和unchecked。unchecked異常一般出現在泛型產生之前的遺留代碼中。爲抑制多個類別的警告,可以使用:

@SuppressWarnings({"unchecked", "deprecation"})

@SafeVargs@SafeVarargs註解,當應用在方法或構造函數中,會假定對於varargs參數,代碼不會執行潛在的不安全的操作,該註解會抑制跟varargs有關的unchecked警告。

FunctionallInteface@FunctionallInteface,出現在Java SE 8中,表示該註解聲明的類型是一個函數接口。

應用在註解上的註解

應用在其他註解上的註解被稱爲 meta-annotations,(元註解?). meta-annotations一般定義在java.lang.annotation.

@Retention@Retention指定被標註的註解如何存儲:

  • RetentionPolicy.SOURCE - 表示被標註的註解只保持在源代碼層面,會被編譯器忽略。

  • RetentionPolicy.CLASS - 表示被標註的註解會在編譯時被編譯器保持,但是會被JVM忽略。

  • RetentionPolicy.RUNTIME - 表示被標註的註解會被JVM保持,所以可以在運行時使用。

@Documented@Documented表示被標註的註解無論用在什麼元素上,該註解都會被Javadoc工具生成在文檔中。(默認情況下,註解不會被包括在Javadoc中)。詳細說明請參考Javadoc tools page

@Target@Target表示被標註的註解會被限制在那種Java元素上使用。 目標註解類型如下:

  • ElementType.ANNOTATION_TYPE 表示可以應用在註解類型上。

  • ElementType.CONSTRUCTOR 表示可以應用在構造函數上。

  • ElementType.FIELD 表示可以應用在變量或屬性上。

  • ElementType.LOCAL_VARIABLE 表示可以應用在本地變量上。

  • ElementType.METHOD 表示可以應用在函數上。

  • ElmentType.PACKAGE 表示可以應用在包聲明上。

  • ElementType.PARAMETER 表示可以應用在方法參數上。

  • ElementType.TYPE 表示可以應用在類的任何元素上。

Inherited@Inherited 表示該註解類型可以從超類繼承。(默認是否)。當用戶查找註解類型,但是類中並沒有應用該註解,則該類的超類也會被查詢。該註解只能應用在類聲明上。

@Repeatable@Repeatable在Java SE 8中引入,表示該註解可以在同一個聲明或類型上多次使用。詳細說明見Repeating Annotations

類型註解和可插拔類型系統

在Java SE 8發佈之前,註解只能用於聲明。 之後,註解可以用於任何類型使用。這意味着註解可以在你使用類型的任何地方使用。一些簡單的例子,如類實例創建(new), casts, implements,以及 throw。 這些形式的註解被稱爲類型註解。

類型註解支持提高了Java程序的強類型檢查的分析能力。Java SE 8並沒有提供類型檢查框架,但是它允許你實現或者下載類型檢查框架,作爲一個或多個可插拔模塊,用於與Java編譯器的連接。

比如,你想確保在你的程序中一個特定的變量永遠不會被設爲null; 你想避免觸發NullPointerException。 你可以寫一個自定義插件來檢查這個變量。你可以用註解標記該變量,表示它不能被賦值爲null。這個變量聲明類似於:

@NonNull String str;

當你編譯該代碼,並在命令行中引入NotNull模塊時,如果編譯器檢查到潛在的錯誤時,編譯器就會打印警告,提醒你修改代碼避免錯誤。當做完修改移除警告時,當程序運行該特定錯誤就不會發生。

可以使用多個類型檢查模塊,每個模塊檢查不同的錯誤。用這種方式,你可以在Java類型系統之上構建,當你想需要的時候可以使用它們進行特殊類型檢查。

在很多情況下,你把需要編寫自己的類型檢查模塊。有許多三方爲你做了這些工作。比如,你可以使用華盛頓大學的Checker Framework。該框架包括NotNull模塊,以及一些正則表達式模塊。詳見Checker Framework

5、重複註解##

在一些情況下,你需要在一個聲明或類型上應用同一個註解。在Java SE 8發佈之後,
reapeating annotations可以讓你實現這些功能。

比如,你想使用定時服務以在給定的時間或時間表執行某個函數,類似於UNIX的cron。現在你可以使用一個定時器去執行函數doPeriodicCleanup, 在每個月的最後一天和每個週五的晚上11點執行。爲了執行這個定時器,創建一個@Schedure註解,並且在doPeriodicCleanup函數上應用兩次。第一次指定每個月的最後一天,第二次指定每週五的晚上11點,示例如下:

@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }

你也可以在任意使用註解的地方使用重複註解。比如,你想在一個類上處理未授權訪問異常。你可以使用@Alert註解標註該類:

@Alert(role="Manager")
@Alert(role="Administrator")
public class UnauthorizedAccessException extends SecurityException { ... }

兼容性考慮,重複註解一般會存儲在由編譯器生成的容器註解中(container annotation)。 爲了讓編譯器作者,需要加兩個聲明.

步驟1: 聲明一個重複註解類型

註解類型必須被@Repeatable元註解標註。 下面的示例定義了自定義註解@Schedule爲重複註解類型:

import java.lang.annotation.Repeatable;

@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}

@Repeatable元註解的值,是編譯器生成的容器註解,用於存儲重複註解。在這個例子中,容器註解類型爲 Schedules, 所以 @Schedule 註解存儲在 @Schedules 中。

步驟2: 聲明一個容器註解類型

容器註解類型必須包含一個value屬性,並且是數組。 數組的類型必須爲可重複的註解類型。聲明 Schedules 容器註解類型如下:

public @interface Schedules {
    Schedule[] value();
}

查找註解

在反射接口中有許多方法用於查找註解。有個返回單個註解的方法,如 AnnotatedElement.getAnnotation(Class), 如果有一個需要查找的類型存在,就返回單個註解。 如果有多個註解出現,你可以通過容器註解類型間接獲取它們。 在這種方式下,老的代碼也可以正常工作。 在Java SE 8中也存在其他的方法可以一次返回多個註解,如 AnnotatedElement.getAnnotationsByType(Class).更詳細的說明見AnnotatedElement.

設計考慮

當設計一個註解類型時,你必須考慮那個類型的基準註解。 現在可以使用一個註解0次,一次,或者,如果一個註解被標註爲 @Repeatable,多次。 你可以使用 @Target 元註解限制該註解類型的使用。比如,你可以創建一個重複註解只能使用再方法和域變量上。 非常重要的一點,你必須儘可能的使註解在使用時使編碼更靈活和更強大。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章