Java 注解(Annotation)又稱之為 Java 標註、元數據,是 Java 1.5 之後加入的一種特殊語法,通過注解可以標註 Java 中的類、方法、屬性、參數、包等,可以通過反射原理對這些元數據進行訪問,注解的使用不會影響程序的正常運行,只會對編譯器警告等輔助工具產生影響,主要內容如下:
- 注解功能
- 定義注解
- 內置注解
- 元注解
- 類型注解
注解功能#
- 編譯器可以使用注解來檢測錯誤和取消警告;
- 使用注解可以生成特定代碼,如
ButtferKnife
使用注解簡化findViewById
等; - 某些注解可以在運行時進行檢查和操作。
定義注解#
注解的定義使用 @interface
作為關鍵字,實際上表示自動繼承了 java.lang.annotation.Annotation
接口,定義格式參考如下:
@元注解
public @interface AnnotationName{
//配置參數(參數類型 參數名稱())
String name() default "hello";
}
配置參數裡面的類型包括基本類型、String
、class
、枚舉以及相關類型的數組,可以使用 default
設置配置參數的默認值,定義一個注解具體如下:
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestDefineAnnotation {
String[] name() default "test";
}
內置注解#
- @Override
- @Deprecated
- @SuppressWarnings
下面是上面三個內置注解的聲明:
//表示當前的方法將覆蓋超類中的方法,編譯時進行格式檢查
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
//表示一個類或者是方法不再建議使用,將其標記為過時,但還是可以使用
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
//表示關閉不當的編譯器警告信息
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
根據對上面三個注解的聲明來看,@SuppressWarnings
中定義了一個數組,這個數組表示在該注解上具體的目標是那些,如可在 SuppressWarnings
上使用的值,常用的具體如下:
deprecation
:使用了過時的類或方法時的警告unused
:有未使用的變量時的警告unchecked
:執行了未檢查的轉換時的警告fallthrough
:當switch
程序塊直接通往下一種情況而沒有break
時的警告- path:在類路徑、源文件路徑等中有不存在的路徑時的警告
- serial:當在可序列化的類上缺少
serialVersionUID
定義時的警告 - finally :任何
finally
子句不能正常完成時的警告 - all:關於以上所有情況的警告
下面看一個案例,具體如下:
public void test() {
long date = Date.parse("2018-04-22");
}
上面的代碼如果使用 eclipse 等其他 IDE 時會出現兩個警告,一是使用了過時的 API,二是變量 date
賦值後沒有被使用過,警告截圖如下:
當然, IDE 會提示是否添加 SuppressWarnings
來取消這些警告,前文中可以看到注解 @SuppressWarnings
的聲明中需要配置參數,這個參數是一個數組,數組名稱是 value
,可以省略這個名稱, 具體如下:
//不省略
public void test2() {
@SuppressWarnings(value= {"deprecation", "unused"})
long date = Date.parse("2018-04-22");
}
//省略
public void test2() {
@SuppressWarnings({"deprecation", "unused"})
long date = Date.parse("2018-04-22");
}
來張截圖說明一下使用 @SuppressWarnings 的效果,具體如下:
如果只想取消一種警告可以這樣寫,具體如下:
//第一種
public void test2() {
@SuppressWarnings(value = {"deprecation"})
long date = Date.parse("2018-04-22");
System.out.println(date);
}
//第二種
public void test2() {
@SuppressWarnings({"deprecation"})
long date = Date.parse("2018-04-22");
System.out.println(date);
}
注意:如果在定義注解的配置參數名稱為 value,那麼可以在配置注解時可以省略 value,反之,使用其他名稱,則必須採用第一種方式,要指定配置參數名稱。
當然其他注解和 @SuppressWarnings
也比較類似, @Override
、@Deprecated
由它們的聲明可知直接使用即可,不需要指定具體目標,在其聲明注解時用到了 @Documented
、@Retention
、@Target
等,這些用來注解其他注解的特殊注解稱之為元注解,具體請看下文。
元注解#
- @Target
- @Retention
- @Documented
- @Inherited
@Target#
@Target
用來描述注解的使用範圍,它的聲明如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
由 @Target
聲明可知使用 @Target
注解必須指定具體的 Java 成員,也就是該注解要使用到哪個位置,具體由枚舉 ElementType
中定義,具體如下:
public enum ElementType {
TYPE, //類、接口、注解、枚舉
FIELD, //屬性(包括枚舉常量)
METHOD, //方法
PARAMETER, //參數
CONSTRUCTOR, //構造方法
LOCAL_VARIABLE, //局部變量
ANNOTATION_TYPE,//注解
PACKAGE, //包
/**
* 類型注解
* @since 1.8
*/
TYPE_PARAMETER,
TYPE_USE
}
@Retention#
@Retention 表示在什麼級別保存該注解的信息,它的聲明如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
由 @Retention 的聲明可知,使用 @Retention 時,必須指定保存 celue(RetentionPolicy),具體值如下:
public enum RetentionPolicy {
SOURCE, //在編譯時會被丟棄,僅僅在源碼中存在
CLASS, //默認策略,運行時就會被丟棄,僅僅在 class 文件中
RUNTIME //編譯時會將注解信息記錄到class文件,運行時任然保留,可以通過反射獲取注解信息
}
@Documented 和 @Inherited 都沒有配置參數,是一種標記注解,@Documented 表示將該注解顯示到用戶文檔中,@Inherited 表示該注解只有使用在類上才會有效,而且該注解會被子類繼承。
類型注解#
在對元注解的說明中可知從 Java8 開始新增了類型注解,如果在注解 @Target
使用這種注解,表明該注解可以在對應的任何地方使用,如在 @Target
中指定 TYPE_PARAMETER
就可在自定義類型的聲明處使用該注解,如在 @Target
中指定 TYPE_USE 就可在任何類型前添加該類之間,主要是方便 Java 開發者使用類型注解和相關插件(Checker Framework)來檢查來在編譯期檢查運行時的異常。
下面分別定義指定 TYPE_PARAMETER 和 TYPE_USE
的注解,具體如下:
//1. TYPE_PARAMETER
@Target(value = {ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {
String value();
}
//2. TYPE_USE
@Target(value = ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeUseAnnotation {
}
然後,在下面的案例中使用這兩個注解,具體如下:
/**
* 測試注解
* @author jzman
*/
public class TestAnnotation {
//...
/**
* ElementType.TYPE_PARAMETER
* 使用在自定義類型聲明的時候,如注解@TypeParameterAnnotation
* @param <T>
*/
static class TypeAnnotationA<@TypeParameterAnnotation(value="hello") T>{
/**
* ElementType.TYPE_USE
* 可以使用在任意類型前面(包含TYPE_PARAMETER)
*/
//創建實例
MyType myType = new @TypeUseAnnotation MyType();
//對象類型
Object obj = (@TypeUseAnnotation Object) myType;
//泛型
ArrayList<@TypeUseAnnotation T> list = new ArrayList<>();
//參數中的類型
public String testA(@TypeUseAnnotation String test) {
return "Hello"+test;
}
//枚舉
public void testB(@TypeUseAnnotation Color color) {
//...
}
enum Color{
RED, GREEN, BLUE
}
}
static class MyType{}
}
其實注解的語法比較簡單,僅僅定義注解對實際開發是沒有幫助的,覺得注解只有在運行時通過反射獲取注解信息才是最重要的,注解與反射相關的內容會在以後的推文中學習,到此對注解的認識就結束了。