Java注解
注解是JDK5引入的一個特性,可以為我們的代碼打上一個標(biāo)記,配合java中的反射會有意想不到的驚喜。我們經(jīng)常用到很多框架都是用到了該特性。所有的注解都繼承于Annotation。
Java 8以前,注解只能在聲明的地方使用(聲明方法、變量、類等),Java 8開始,注解可以在任何地方使用(泛型、拋異常、方法中等),這是因為Java 8 增加了兩種ElementType(下文會介紹)。
- 備注:代碼基于Android API 26 中的java代碼以及Java 8中的代碼(Mac>>Android studio 3.3.2)。
注解的示例
- 自定義注解代碼示例:
/**
* 測試注解
* TestApplication
* Created by anonyper on 2019/5/30.
*/
@Retention(RetentionPolicy.RUNTIME)//屬于運(yùn)行時注解
@Target(ElementType.METHOD)//適用于方法注解
public @interface TestAnnotationMethod {
String LabelType() default "default";//注解中LabelType 取值
}
- 系統(tǒng)內(nèi)置注解代碼示例:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- 使用代碼示例:
public class RachelApplication extends Application {
//系統(tǒng)注解
@Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent(this, LocationService.class);
startService(intent);
}
//自定義注解
@TestAnnotationMethod(LabelType = "Wifi")
public void testMethod() {
}
}
注解基本語法介紹
從示例中可以看到,使用@interface即可標(biāo)示這是一個注解,一個注解包括元注解和注解屬性兩部分。下面我們分別來介紹元注解、Java內(nèi)置的常用注解以及注解屬性。
注解屬性
注解起到一種標(biāo)記作用,有的時候我們需要給定不同的屬性,比如上面的String LabelType() default "default";//注解中LabelType 取值這段代碼。這是用來給一個注解指定一個屬性。比如我們用到的@Target注解,里面的值是一個枚舉,代表著該注解的不用使用范圍。再比如,我們自定義一個注解用于標(biāo)示人物身份,那么身份的值就可能包含:老師、學(xué)生、企業(yè)家、律師、醫(yī)生等等。
注解的屬性也可以叫做成員變量(注解內(nèi)不能有方法的),定義屬性的時候需要用無形參的方式來聲明,并且方法名代表變量名字,返回值代表屬性的類型(屬性類型可為:8種基本類型(byte字節(jié)型、short短正型、int整型、long長整型、float單精度浮點型、double雙精度浮點型、boolean布爾型、char字符型)、Class類型、String、枚舉、注解以及他們的數(shù)組)。
- 屬性的使用:
@Retention(RetentionPolicy.RUNTIME)//運(yùn)行時注解
@Target(ElementType.TYPE)//適用于方法注解
public @interface TestAnnotation {
String LabelType() default "guanwang" ;
}
//使用:
@TestAnnotation(LabelType = "Wifi")
public class RachelApplication{
}
//或者
@TestAnnotation//默認(rèn)是guanwang
public class RachelApplication{
}
//或者
@TestAnnotation()//默認(rèn)是guanwang
public class RachelApplication{
}
- value
value算是一個特殊的屬性,如果一個注解中只有value屬性需要設(shè)置(沒有其他屬性或其他屬性有默認(rèn)值),在設(shè)置時value=可以省略。
@Retention(RetentionPolicy.RUNTIME)//運(yùn)行時注解
@Target(ElementType.TYPE)//適用于方法注解
public @interface TestAnnotation {
String LabelType() default "guanwang" ;
String value();
}
//使用
@TestAnnotation("GPRS")
public class RachelApplication{
}
- 其他類型屬性
@Retention(RetentionPolicy.RUNTIME)//運(yùn)行時注解
@Target(ElementType.TYPE)//適用于方法注解
public @interface TestAnnotation {
int index() default 1;//int 類型
boolean flag() default false;//布爾類型
String LabelType() default "guanwang";//字符串
int[] arrayint() default {1, 2, 3, 4};//數(shù)組
ElementType elementType() default ElementType.TYPE;//枚舉類型 用了ElementType做示例
Target target() default @Target(ElementType.TYPE); //注解類型 這里用了Target做示例
Class<?> classType() default Integer.class;//class類型
String value();
}
元注解
元注解就是可以標(biāo)記注解的注解,我們在聲明注解使用的范圍@Target,生存周期@Retention等我們常用的,還有一些諸如@Documented、@Inherited、@Repeatable等元注解,我們接下來一一說明他們各自的含義:
@Target
限定注解的作用范圍,比如方法、類、變量、等,具體如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Target {
ElementType[] value();
}
//ElementType 代碼如下
public enum ElementType {
ANNOTATION_TYPE,//可以用在另一個注解上(自定義元注解)
CONSTRUCTOR,//可以用于構(gòu)造方法上
FIELD,//可以用于字段聲明上,包括枚舉上
LOCAL_VARIABLE,//可以用于局部變量上
METHOD,//可以用于方法上
PACKAGE,//可以用于包聲明上
PARAMETER,//可以用于參數(shù)聲明上
TYPE,//可以用于類、接口、枚舉聲明上
TYPE_PARAMETER,//表示注解能寫在類型變量的聲明語句中。1.8版本加入的 網(wǎng)上的示例比如
//List<Integer> list = new @Save ArrayList<>(); 但是我在測試過程中報錯(Mac AndroidStudio 3.3.2 Android 26 java 1.8 ),唯一測試可用的地方:public class Book<@TestAnnotation T>
TYPE_USE;//表示注解能寫在使用類型的任何語句中 1.8版本加入 如
/**
public class Book<@TestAnnotation T> {
List<Integer> list = new @TestAnnotation ArrayList<>();
@TestAnnotation int index = 10;//默認(rèn)10本書
public @TestAnnotation int getIndex() {
return 1;
}
}
*/
private ElementType() {
}
}
Java 8 新增的兩個類型注解的作用是用來強(qiáng)健java代碼,配合第三方工具(如Checker Framework)做強(qiáng)類型檢查,可以在編譯期檢測出異常(如UnsupportedOperationException、NullPointerException異常等),避免了程序在運(yùn)行時才拋出錯誤,這就是類型注解的主要作用。
@Retention
用來標(biāo)示該注解的生存周期,比如運(yùn)行時注解、編譯時注解等。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Retention {
RetentionPolicy value();
}
//RetentionPolicy 代碼如下
public enum RetentionPolicy {
CLASS,//標(biāo)示注解只被保留到編譯的時候,不會被加載到JVM中
RUNTIME,//標(biāo)示注解可以保留到運(yùn)行時,會被加載到JVM中
SOURCE;//標(biāo)示注解只是在源代碼階段,在編譯前會被忽略
private RetentionPolicy() {
}
}
@Documented
標(biāo)示將注解的元素包含到j(luò)avadoc中。(我們可以把我們的代碼生成API文檔的哦)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Documented {
}
@Inherited
標(biāo)示改注解可以被繼承,如果一個父類使用該注解,子類沒有做其他注解時,默認(rèn)繼承了父類的注解。
@Inherited //該注解可以被繼承
@Retention(RetentionPolicy.RUNTIME)//運(yùn)行時注解
@Target(ElementType.TYPE)//適用于類注解
public @interface TestAnnotationType {
}
//父類使用注解,該注解有Inherited標(biāo)示
@TestAnnotationType
class ParentClass {
}
//子類默認(rèn)繼承父類的注解
public class ChildClass extends ParentClass {
}
@Repeatable(Java 8增加的)
標(biāo)示該注解在一個地方可重復(fù)使用,可以取不同的值,如一個人的身份可以是律師,他還兼職老師的工作。需要傳入一個繼承注解的類來作為其容器。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Repeatable {
Class<? extends Annotation> value();
}
//使用方法:
//注解容器
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Identitys {
Identity[] value();//必須有一個方法名為 value 返回的類型用的是使用Repeatable的注解
}
//聲明可重復(fù)
@Repeatable(Identitys.class)//容器類是Identitys
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Identity {
String label();
}
//具體使用
@Identity(label = "student")
@Identity(label = "teacher")
public class TestRepeatable {
}
Java內(nèi)置常用注解
我們在繼承一個類,子類重新父類方法時是不是用到@Override,在看到老版本廢棄的方法時,是否看到@Deprecated呢,這些都是Java內(nèi)置的注解,常用的有:
@Override
標(biāo)示子類復(fù)寫了父類的方法用到的修飾。防止父類方法名改變時,子類方法忘記做對應(yīng)調(diào)整。
@Deprecated
標(biāo)示過時的元素,雖然可以調(diào)用,但不建議使用的意思,使用的時候,在IDE上會顯示一條灰色的線。
@SuppressWarnings
阻止編譯器對被標(biāo)記方法、類發(fā)出警告,里面需要傳入阻止警告的哪些類型,常用的:all(所有警告)、unchecked(沒有進(jìn)行類型檢查操作的警告)、unused(沒有使用過的代碼警告),deprecation(使用過期方法)。所以在調(diào)用過時方法的方法前面加上@SuppressWarnings("deprecation"),那條灰色就不會顯示了。
具體SuppressWarnings類型值的解釋,可以自行百度。
@SafeVarargs (Java 7新增)
Java SE 5中引入的可變形參加上Java編譯器的機(jī)制,導(dǎo)致在使用可變形參的時候,編譯器會產(chǎn)生一個未經(jīng)檢查的警告,SafeVarargs注解就可以忽略這些警告。SafeVarargs注解只能用在參數(shù)長度可變的靜態(tài)方法(或用final修飾)或構(gòu)造方法上,否則會出現(xiàn)編譯錯誤。(這些只是屏蔽了編譯時的警告,如果類型轉(zhuǎn)化錯誤,在運(yùn)行時還會報錯的)。
@FunctionalInterface(Java 8新增)
該注解標(biāo)示該類是一個函數(shù)式的接口,就是里面除了Object類中public修飾的方法外只有一個抽象方法。比如 View.OncClickListener,Runnable等都可以使用FunctionalInterface標(biāo)記一下。這些函數(shù)式接口可以使用lambda表達(dá)式來編寫。
注解的使用
上面介紹了注解的基本知識,我們學(xué)會了如何對一個元素用注解進(jìn)行標(biāo)記,那么接下來我們就要考慮標(biāo)記之后如何識別呢?這些內(nèi)容因為篇幅原因,我們就另開一篇分析。