1. 程式人生 > >Java基礎教程(16)--註解

Java基礎教程(16)--註解

保留 使用 jpg ext tty 拋出異常 alt 元素 turn

一.註解基礎知識

1.註解的格式

??最簡單的註解就像下面這樣:

@Entity

??@符號指示編譯器其後面的內容是註解。在下面的例子中,註解的名稱為Override:

@Override
void superMethod() {...}

??註解可以有若幹個屬性。可以在使用註解時指定屬性的值:

@Auther(name = "maconn")
class MyClass {
    ...
}

??如果註解只有一個屬性,則可以省略屬性的名稱:

@Auther("maconn")
class MyClass {
    ...
}

??如果註解沒有屬性,或不需要指定屬性的值,則可以省略括號,就像上面的@Override註解。
??可以在一個聲明上使用多個註解:

@Auther("maconn")
@Entity
class MyClass {
    ...
}

??有些一個註解可以在一個聲明上使用多次,這種註解稱為重復註解:

@Author("maconn")
@Author("anotherPerson")
class MyClass { ... }

??重復註解時Java8引入的特性。稍後我們將會講解更多有關重復註解的內容。
??可以使用java.lang或java.lang.annotation包中的註解,例如前面的Override就是Java中預定義的註解;也可以定義自己的註解類型,前面的例子中,Entity和Author就是自定義的註解。

2.在什麽地方使用註解

??註解可以用在類型的聲明上面,例如類的聲明,域的聲明,方法的聲明等。從Java8開始,註解還可以用在使用這些類型的地方,下面是幾個例子:

  • 創建對象時:
new @Interned MyObject();
  • 類型轉換:
myString = (@NotNull String) str;
  • 實現接口時:
class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... }
  • 拋出異常時:
void monitorTemperature() throws @Critical TemperatureException { ... }

??這種註解稱為類型註解,稍後將進行深入討論。

二.自定義註解

??註解可以用來替換代碼中的部分註釋。假設一個項目中的每個類都需要一些包含重要信息的註釋:

public class Generation3List extends Generation2List {
   // Author: maconn
   // Date: 2018/12/23
   // Current revision: 1.0
   // Last modified: 2018/12/23

   ...
}

??要使用註解添加相同的元數據,必須先定義註解:

@interface ClassPreamble {
   String author();
   String date();
   double currentRevision() default 1;
   String lastModified() default "N/A";
}

??註解的定義看上去有點像接口的定義,只不過interface關鍵字前面多了一個@符號。註解實際上是一種特殊的接口,有關這部分的內容會在後面介紹到。
??上面的註解定義中包含了註解屬性的聲明,看上去就像定義了一個方法一樣。可以為屬性定義默認值。
??定義好註解之後,就可以向下面這樣使用了:

@ClassPreamble (
   author = "maconn",
   date = "2018/12/23",
   currentRevision = 1.0,
   lastModified = "2018/12/23",
)
public class Generation3List extends Generation2List {
    ...
}

三.預定義註解

??在Java API中已經為我們預先定義了幾個註解。這其中有幾個是供Java編譯器使用的,還有些註解是用在別的註解上的。

給Java編譯器使用的註解

@Deprecated:@Deprecated註解表示被標記的元素已經被棄用或者說不再推薦使用。如果在程序中使用帶有@Deprecated註解的方法、類或域,編譯器就會給出警告。當一個元素被棄用時,也應該在同時在Javadoc中使用@deprecated(註意Javadoc中的@deprcated首字母是小寫)標記,就像下面這樣:

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

@Override:@Override註解告訴編譯器該元素旨在覆蓋超類中聲明的元素。假設超類中聲明了一個int overriddenMethod()方法,當在子類中重寫這個方法時,可以加上@Override註解,就像下面這樣:

@Override
int overriddenMethod() {
    ...
}

??雖然在重寫方法時不需要使用此批註,但使用它有助於防止出錯。如果@Override標記的方法無法正確覆蓋其超類中的方法,則編譯器會給出錯誤。
@SuppressWarnings:@SuppressWarnings註解可以讓編譯器忽略它指定的警告。在以下示例中,使用了不推薦使用的方法,編譯器通常會生成警告。但是,在添加@SuppressWarnings註解後,編譯器將不再給出警告。

@SuppressWarnings("deprecation")
void useDeprecatedMethod() {
    objectOne.deprecatedMethod();
}

??每個警告都屬於一個類別。在Java中警告有兩個類別:deprecation和unchecked。如果要同時忽略這兩種警告,可以使用以下語法:

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

@SafeVarargs:當應用於方法或構造函數時,@SafeVarargs註解斷言代碼不會對可變參數列表執行潛在的不安全操作。使用此註註解時,有關可變參數列表的unchecked警告將會被忽略。

@SafeVarargs  
public static <T> T void useVarargs(T... args) {  
    return args.length > 0 ? args[0] : null;  
} 

@FunctionalInterface:@FunctionalInterface註解是Java8引入的註解,作用在接口上以表明該接口是函數式接口(函數式接口是指只有一個抽象方法的接口)。

作用於其他註解的註解

??作用於其他註解的註解被稱為元註解。在java.lang.annotation包中定義了以下幾個元註解:
@Retention:@Retention註解用於定義註解的保留策略:

  • RetentionPolicy.SOURCE - 註解僅存在於源碼中,編譯時將會被忽略。
  • RetentionPolicy.CLASS - 註解會在class字節碼文件中存在,但會被JVM忽略。
  • RetentionPolicy.RUNTIME - 註解會被JVM保留,因此可以在運行時環境使用。

@Documented:@Documented註解用來定義被標註的註解在使用時是否會出現在Javadoc文檔中。考慮下面的例子:

import java.lang.annotation.Documented ;

@MyAnnotation(name="maconn")
public class AnnotationDemo{
    public void foo() {}
}

@Documented
@interface MyAnnotation{
    public String name();
}

??在上面的例子中,我們自定義了一個註解MyAnnotation,註意它的定義上有一個@Documented註解。我們在AnnotationDemo類上使用了@MyAnnotation,然後這個源文件使用javadoc命令提取文檔,結果如下:

技術分享圖片
??可以看到,AnnotationDemo類上的註解@MyAnnotation出現在了文檔中。正常情況下,沒有@Documented註解的註解是不會出現在文檔中的。下面的例子中,我們去掉MyAnnotation註解上的@Documented:

import java.lang.annotation.Documented ;

@MyAnnotation(name="maconn")
public class AnnotationDemo{
    public void foo() {}
}

@interface MyAnnotation{
    public String name();
}

??然後重新生成文檔:

技術分享圖片
??可以看到,@MyAnnotation註解並沒有出現在文檔裏。
@Target:@Target註解作用在另外一個註解上用來限制這個註解可以用在哪些類型上:

  • ElementType.ANNOTATION_TYPE 可以應用於註解類型。
  • ElementType.CONSTRUCTOR 可以應用於構造函數。
  • ElementType.FIELD 可以應用於域。
  • ElementType.LOCAL_VARIABLE 可以應用於局部變量。
  • ElementType.METHOD 可以應用於方法。
  • ElementType.PACKAGE 可以應用於包聲明。
  • ElementType.PARAMETER 可以應用於方法的參數。
  • ElementType.TYPE 可以應用於類,接口或枚舉類型。

@Inherited:@Inherited註解表明子類可以繼承此註解,如果一個類使用此註解,則它的子類也繼承此註解。此註解僅適用於類聲明。
@Repeatable:@Repeatable是Java8中引入的註解。@Repeatable註解標記的註解可以在一個類型上使用多次。

四.重復註解

??有些時候,可能需要將多個相同的註解用在一個類型上。從Java8開始,可以使用重讀註解做到這一點。例如,假設我們要編寫一個定時任務。現在要設置定時器在每個月的最後一天和每個周五的23:00運行方法doSomething。要設置定時器,需要創建一個@Schedule註解並將其應用於doSomething方法兩次。如下面的代碼所示:

@Schedule(dayOfMonth =“last”)
@Schedule(dayOfWeek =“Fri”,hour =“23”)
public void doSomeThing() {
    ...
}

??出於兼容性的原因,重復註解被存儲在由Java編譯器生成的容器註解內。為了使編譯器執行此操作,需要以下兩個步驟:

第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註解的值,是用來存儲這個重復註解的容器註解。在這個例子中,@Repeatable註解的值是Schedules.class,因此重復註解@Schedule都存儲在@Schedules中。
??如果在同一個類型上使用多個相同的註解並且這個註解不是重復註解時,將會產生編譯時錯誤。

第2步:聲明容器註解

??容器註解必須有一個數組類型的value元素,且數組元素的類型必須是一個可重復註解。下面聲明了容器註解@Schedules:

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

Java基礎教程(16)--註解