本系列文章主要目的在于重新梳理 Java 基礎(chǔ)知識(shí),可能不會(huì)按照由淺入深的順序去寫。文章內(nèi)容主要參考《Core Java for the Impatient》一書。
注解相關(guān)的內(nèi)容打算分兩篇來寫:基礎(chǔ)篇和進(jìn)階篇,本文是基礎(chǔ)篇,主要涵蓋以下內(nèi)容:
- 什么是注解
- 注解的基本使用及分類
- 標(biāo)準(zhǔn)注解與自定義注解
#0. 什么是注解
注解(Annotation)是 Java 在 1.5 版本中引入的新特性。它可以用來標(biāo)注源碼,并在編譯期或運(yùn)行時(shí)被注解處理器處理。如果你是一個(gè)優(yōu)雅的 Android Developer,那么你肯定對(duì) ButterKnife、retrofit 等優(yōu)秀的三方庫非常熟悉。你沒看錯(cuò),它們都在使用注解讓代碼變的更優(yōu)雅,易于理解。下面就讓我們揭開注解神秘的面紗吧。
Are You Ready?
#1. 注解的基本使用及分類
在正式了解注解之前,我們先看一個(gè)例子:學(xué)過 Java 的同學(xué)應(yīng)該都知道,在自定義對(duì)象中,我們需要重寫 toString
方法以格式化打印該對(duì)象時(shí)的輸出。
public class Person {
String name;
int age;
...
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
相信你已經(jīng)注意到了 toString
方法上面的 @Override
,它就是一個(gè)標(biāo)記在方法上的注解,用于表示該方法是對(duì)父類方法的重寫。
#1.1. 注解的基本使用
讓我們借著上面的例子來講一下注解的基本使用。如你所見,注解都是以 @
開頭,后面緊跟一個(gè)標(biāo)準(zhǔn)或自定義注解類型(這里的 Override
是一個(gè)標(biāo)準(zhǔn)注解)。
注解可以聲明在源碼中的以下位置:
- 類,枚舉,接口
- 方法
- 構(gòu)造器
- 實(shí)例(全局)變量(包括枚舉常量)
- 局部變量(包括
for
或try
代碼塊中的變量) - 形式參數(shù)和
catch
中的參數(shù) - 類型參數(shù)
- 包
// 類注解
@Entity public class User{...}
// 變量注解
@SuppressWarnings("unchecked") List<User> users = ...;
public User getUser(@Param("id") String userId){...}
// 類型參數(shù)注解
public class Cache<@Immutable V> {...}
// 包注解
@GPL(version=“3”)
package com.horstmann.corejava;
import org.gnu.GPL; // 注意:導(dǎo)入語句在包定義下方
注解可以擁有一些被稱為元素的鍵值對(duì),以 JUnit 4 測(cè)試框架中的 @Test
注解為例:
@Test(timeout=2000)
public void testSample() {
...
}
此處的 timeout=2000
表示該測(cè)試最長可執(zhí)行 2000 毫秒,超過 2000 毫秒沒有執(zhí)行完就會(huì)拋出異常:
java.lang.Exception: test timed out after 2000 milliseconds
如果注解中只有一個(gè)元素,且元素的鍵名為 value
,則該元素的鍵可以省略,如上面的變量注解:
@SuppressWarnings("unchecked") List<User> users = ...;
元素的值可以是以下類型:
- 原始類型
- 字符串
- Class 對(duì)象
- 枚舉實(shí)例
- 注解
- 或以上類型的數(shù)組(不包括數(shù)組)
在同一個(gè)條目上可以有多個(gè)(相同的或不同的)注解。
@Test
@BugReport(showStopper=true, reportedBy=“Joe”)
public void checkRandomInsertions() { ... }
@BugReport(showStopper=true, reportedBy=“Joe”)
@BugReport(reportedBy={“Harry”, “Carl”})
public void checkRandomInsertions() { ... }
#1.2. 注解的分類
根據(jù)注解保留的時(shí)期,可以將其分為三類:
- 源碼期注解:只能保留在源碼文件中,不會(huì)被編譯到 .class 文件
- 編譯期注解:只能被編譯器保留到 .class 文件中,不能被虛擬機(jī)保留到運(yùn)行時(shí)
- 運(yùn)行時(shí)注解:能被虛擬機(jī)保留到運(yùn)行時(shí)
作用于注解類型上的注解被稱為元注解。
#2. 標(biāo)準(zhǔn)注解與自定義注解
Java 默認(rèn)實(shí)現(xiàn)了一些方便的注解類型供我們?cè)陂_發(fā)中使用,它還允許我們?cè)谛枰獣r(shí)創(chuàng)建自定義注解。
#2.1. Java 內(nèi)置標(biāo)準(zhǔn)注解
Java 的內(nèi)置注解的使用范圍及其作用可總結(jié)為下表:
需要注意的是最下面的五個(gè)注解,它們都是用在注解類型上的,是元注解。
#2.2. 自定義 Java 注解
與使用 class
聲明類相似,Java 使用 @interface
聲明一個(gè)注解類型:
public @interface BugReport { ... }
我們可以像給接口添加方法一樣為自定義注解添加元素:
public @interface BugReport {
String desc();
String[] reportBy() default {}; //添加默認(rèn)值,如果不指定,則使用該值
}
我們還可以給自定義注解指定保留時(shí)期和使用范圍:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface BugReport {
String desc();
String[] reportBy() default {};
}
這樣,我們的 BugReport 將只能作用于類型級(jí)別上(不能在方法,字段上使用),而且指定其為編譯期注解。
下圖為 @Target
所有可用參數(shù)及其含義:
@Retention
元注解只有三種選擇,分別對(duì)應(yīng)于上文的三個(gè)保留時(shí)期:
- RetentionPolicy.SOURCE:源碼處理時(shí)可見,不會(huì)包含在 .class 文件中
- RetentionPolicy.CLASS:默認(rèn)選擇,會(huì)包含在 .class 文件中,但不會(huì)被虛擬機(jī)加載
- RetentionPolicy.RUNTIME:運(yùn)行時(shí)可見,可以通過反射 API 獲取
以上就是本文的全部?jī)?nèi)容,我們將會(huì)在進(jìn)階篇中講解更多關(guān)于注解的使用技巧,如果你對(duì)本文有任何建議,歡迎留言反饋。