1. 程式人生 > >Java內建系統註解和元註解

Java內建系統註解和元註解

第一節:註解(Annotation)的作用

       Annotation(註解)是JDK5.0及以後版本引入的。它的作用是修飾程式元素。什麼是程式元素呢?例如:包、類、構造方法、方法、成員變數等。

      註解,就是對某一事物進行添加註釋說明,會存放一些資訊,這些資訊可能對以後某個時段來說是很有用處的。
     java提供了一套機制,使得我們可以對方法、類、引數、包、域以及變數等新增標註(即附上某些資訊)。且在以後某個時段通過反射將標註的資訊提取出來以供使用。 
    註解相當於一種標記,在程式中加了註解就等於為程式打上了某種標記。程式可以利用java的反射機制來了解你的類及各種元素上有無何種標記,針對不同的標記,就去做相應的事件。

第二節:定義註解

      定義新的Annotation型別使用@interface關鍵字(在原有interface關鍵字前增加@符號)。定義一個新的Annotation型別與定義一個介面很像,例如:

public @interface Test{
}

定義完該Annotation後,就可以在程式中使用該Annotation。使用Annotation,非常類似於public、final這樣的修飾符,通常,會把Annotation另放一行,並且放在所有修飾符之前。例如:

@Test
public class MyClass{
....
}

根據註解是否包含成員變數,可以把註解分為如下兩類:

  • 標記註解:沒有成員變數的Annotation被稱為標記。這種Annotation僅用自身的存在與否來為我們提供資訊,例如@override等。

  • 元資料註解:包含成員變數的Annotation。因為它們可以接受更多的元資料,因此被稱為元資料Annotation。 成員以無引數的方法的形式被宣告,其方法名和返回值定義了該成員變數的名字和型別。

 成員變數

Annotation只有成員變數,沒有方法。Annotation的成員變數在Annotation定義中以“無形參的方法”形式來宣告,其方法名定義了該成員變數的名字,其返回值定義了該成員變數的型別。例如:

public @interface MyTag{
    string name();
    int age();
}

示例中定義了2個成員變數,這2個成員變數以方法的形式來定義。

一旦在Annotation裡定義了成員變數後,使用該Annotation時就應該為該Annotation的成員變數指定值。例如:

public class Test{
    @MyTag(name="紅薯",age=30)
    public void info(){
    ......
    }
}

也可以在定義Annotation的成員變數時,為其指定預設值,指定成員變數預設值使用default關鍵字。示例:

public @interface MyTag{
    string name() default "我蘭";
    int age() default 18;
}

如果Annotation的成員變數已經指定了預設值,使用該Annotation時可以不為這些成員變數指定值,而是直接使用預設值。例如:

public class Test{
    @MyTag
    public void info(){
    ......
    }
}
如果註解只有一個成員變數,則建議取名為value,在使用時可用忽略成員名和賦值符=

第三節:基本註解

在java.lang包下,JAVA提供了5個基本註解::

  • @Override 

       限定重寫父類方法。對於子類中被@Override 修飾的方法,如果存在對應的被重寫的父類方法,則正確;如果不存在,則報錯。@Override 只能作用於方法,不能作用於其他程式元素。

  • @Deprecated

       用於表示某個程式元素(類、方法等)已過時。如果使用了被@Deprecated修飾的類或方法等,編譯器會發出警告。

  • @SuppressWarnings

        抑制編譯器警告。指示被@SuppressWarnings修飾的程式元素(以及該程式元素中的所有子元素,例如類以及該類中的方法.....)取消顯示指定的編譯器警告。例如,常見的@SuppressWarnings(value="unchecked")

          SuppressWarnings註解的常見引數值的簡單說明:

                1.deprecation:使用了不贊成使用的類或方法時的警告(使用@Deprecated使得編譯器產生的警告);
    2.unchecked:執行了未檢查的轉換時的警告,例如當使用集合時沒有用泛型 (Generics) 來指定集合儲存的型別; 關閉編譯器警告
    3.fallthrough:當 Switch 程式塊直接通往下一種情況而沒有 Break 時的警告;
    4.path:在類路徑、原始檔路徑等中有不存在的路徑時的警告;
    5.serial:當在可序列化的類上缺少 serialVersionUID 定義時的警告;
    6.finally:任何 finally 子句不能正常完成時的警告;
    7.all:關於以上所有情況的警告。

  • @SafeVarargs

       @SafeVarargs是JDK 7 專門為抑制“堆汙染”警告提供的。

  • @FunctionalIterface   (java 8 新增的)

        函式式介面。Java8規定:如果介面中只有一個抽象方法(可以包含多個預設方法或多個static方法),該介面稱為函式式介面。

        @FunctionalInterface就是用來指定某個介面必須是函式式介面,否則就會編譯出錯。

   @FunctionalInterface

public interface Fun

{

   static void foo()

{

    System.out.println("foo類方法");

}

default void bar()

{

    System.out.println("bar預設方法");

}

void test();//只定義了一個抽象方法

}

如在上面的介面中再加一個抽象方法abc(),則會編譯出錯。

第四節 元註解

在java.lang.annotation包下,定義了6個元註解。元註解就是修飾註解的註解。

       拿到一個註解,如何知道它是否是元註解呢?需要看它的元註解(無論是元註解還是普通註解都是有元註解的),如果看到這樣的元註解:@Target(ElementType.ANNOTATION_TYPE),那麼此時這個註解一定是元註解。

  • @Retention

  • @Target

  • @Documented

  • @Inherited

  • @Repeatable  (java 8新增)

  • 型別註解

@Repetable和型別註解在另外一篇博文中介紹。

4.1 @Retention

@Retention用於指定註解可以保留多長時間(生命週期)。

@Retention包含一個名為“value”的成員變數,該value成員變數是RetentionPolicy列舉型別。使用@Retention時,必須為其value指定值。value成員變數的值只能是如下3個:

  • RetentionPolicy.SOURCE:Annotation只保留在原始碼中,編譯器編譯時,直接丟棄這種Annotation,不記錄在.class檔案中。

  • RetentionPolicy.CLASS:編譯器把Annotation記錄在class檔案中。當執行Java程式時,JVM中不可獲取該Annotation資訊。這是預設值

  • RetentionPolicy.RUNTIME:編譯器把Annotation記錄在class檔案中。當執行Java程式時,JVM可獲取該Annotation資訊,程式可以通過反射獲取該Annotation的資訊。

示例:

package com.demo1;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

//name=value形式
//@Retention(value=RetentionPolicy.RUNTIME)

//直接指定
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTag{
	String name() default "我蘭";
}

如果Annotation裡有一個名為“value“的成員變數,使用該Annotation時,可以直接使用XXX(val)形式為value成員變數賦值,無須使用name=val形式。

4.2 @Target

@Target指定Annotation用於修飾哪些程式元素。@Target也包含一個名為”value“的成員變數,該value成員變數型別為ElementType[ ]ElementType為列舉型別,值有如下幾個:

  • ElementType.TYPE:能修飾類、介面或列舉型別

  • ElementType.FIELD:能修飾成員變數

  • ElementType.METHOD:能修飾方法

  • ElementType.PARAMETER:能修飾引數

  • ElementType.CONSTRUCTOR:能修飾構造器

  • ElementType.LOCAL_VARIABLE:能修飾區域性變數

  • ElementType.ANNOTATION_TYPE:能修飾註解

  • ElementType.PACKAGE:能修飾包

示例1(單個ElementType):

package com.demo1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
public @interface AnnTest {
	String name() default "sunchp";
}

示例2(多個ElementType):

package com.demo1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface AnnTest {
	String name() default "sunchp";
}

4.3 @Documented

如果定義註解A時,使用了@Documented修飾定義,則在用javadoc命令生成API文件後,所有使用註解A修飾的程式元素,將會包含註解A的說明。

示例:

@Documented
public @interface Testable {
}
public class Test {
	@Testable
	public void info() {
	}
}

4.4 @Inherited

@Inherited指定註解具有繼承性。如果某個類使用了@xxx註解(定義該註解時使用了@Inherited修飾)修飾,則其子類將自動被@xxx修飾

示例:

package com.demo2;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyTag{

}
package com.demo2;

@MyTag
public class Base {

}
package com.demo2;

//SubClass只是繼承了Base類
//並未直接使用@MyTag註解修飾
public class SubClass extends Base {
	public static void main(String[] args) {
		System.out.println(SubClass.class.isAnnotationPresent(MyTag.class));
	}
}

示例中Base使用@MyTag修飾,SubClass繼承Base,而且沒有直接使用@MyTag修飾,但是因為MyTag定義時,使用了@Inherited修飾,具有了繼承性,所以執行結果為true。

如果MyTag註解沒有被@Inherited修飾,則執行結果為:false。

第五節:註解的語法與定義形式

(1)以@interface關鍵字定義

(2)註解需要標明註解的生命週期,註解的修飾目標等資訊,這些資訊是通過元註解實現。
上面的語法不容易理解,下面通過例子來說明一下,這個例子就是Target註解的原始碼,

1 2 3 4 5 6 @Retention(value=RetentionPolicy.RUNTIME) @Target(value={ElementType.ANNOTATION_TYPE}) public@interfaceTarget { ElementType[]value(); }

原始碼分析如下:
第一:元註解@Retention,成員value的值為RetentionPolicy.RUNTIME。
第二:元註解@Target,成員value是個陣列,用{}形式賦值,值為ElementType.ANNOTATION_TYPE
第三:成員名稱為value,型別為ElementType[]
另外,需要注意一下,如果成員名稱是value,在賦值過程中可以簡寫。如果成員型別為陣列,但是隻賦值一個元素,則也可以簡寫。如上面的簡寫形式為:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)

第六節:註解的底層實現

定義一個註解:

1 2 3 4 5 6 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public@interfaceCache { Stringvalue()default"cache"; }

分析其位元組碼,如下圖所示:
C:\Users\Administrator\workspace\cache\bin>javap -verbose Cache

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 Classfile/C:/Users/Administrator/workspace/cache/bin/Cache.class Last modified2016-7-23;size429bytes MD5checksum771f9c4d63ce2ded5d3b093deebd2c69 Compiled from"Cache.java" publicinterfaceCacheextendsjava.lang.annotation.Annotation minor version:0 majorversion:52 flags:ACC_PUBLIC,ACC_INTERFACE,ACC_ABSTRACT,ACC_ANNOTATION Constantpool: #1 = Class              #2             // Cache #2 = Utf8               Cache #3 = Class              #4             // java/lang/Object #4 = Utf8               java/lang/Object #5 = Class              #6             // java/lang/annotation/Annotation #6 = Utf8               java/lang/annotation/Annotation #7 = Utf8               value #8 = Utf8               ()Ljava/lang/String; #9 = Utf8               AnnotationDefault #10 = Utf8               cache #11 = Utf8               SourceFile #12 = Utf8               Cache.java #13 = Utf8               RuntimeVisibleAnnotations #14 = Utf8               Ljava/lang/annotation/Retention; #15 = Utf8               Ljava/lang/annotation/RetentionPolicy; #16 = Utf8               RUNTIME #17 = Utf8               Ljava/lang/annotation/Target; #18 = Utf8               Ljava/lang/annotation/ElementType; #19 = Utf8               TYPE { publicabstractjava.lang.Stringvalue(); descriptor:()Ljava/lang/String; flags:ACC_PUBLIC,ACC_ABSTRACT AnnotationDefault: default_value:s#10 } SourceFile:"Cache.java" RuntimeVisibleAnnotations: 0:#14(#7=e#15.#16) 1:#17(#7=[e#18.#19])

分析上面的位元組碼,我們可以得出:
第一:
public interface Cache extends java.lang.annotation.Annotation,說明Cache註解是繼承自Annotation,仍然是interface。
第二:
public abstract java.lang.String value(),說明value方法是abstract型別。