Java的註解淺析
人的一生就像一篇文章,只有經過多次精心修改,才能不斷完善
Java註解概念理解:
Java註解又稱為Java標註,是JDK5引入的一中註釋機制,Java中大家熟悉的五種註解分別是:@Override,@Deprecated,@SuppressWarnings,@SafeVarargs,@FunctionalInterface,看到這些註解大家應該都不陌生,這是Java中基本的五個註解。
Java註解可以通俗理解為打標籤,我可以給一個年輕人打一個標籤為年輕,活力,激情,理想主義等,這些就可以看作是年輕人的註解。可以理解標籤是事物某些方面的特點和解釋。
如何定義註解:
定義註解通過@interface關鍵字來實現
public @interface Fruit {
}
如上程式碼,即建立了Fruit註解,可以理解一個名字為Fruit的標籤。
使用註解:使用註解的方法很簡單,可以將註解放在類上或者方法上,看你需要這個註解實現什麼作用了,例如將註解註釋類
@Fruit
public class Annotest {
}
這樣就將@Fruit標籤打到了Annotest這個類上。
看到這,你可能就明白了,註解就是將標籤打到某個方法,類,包上,標識這個類,使這個方法,類具有這個註解所具有的標識唄,是的,沒錯
使用自定義註解僅僅做到這樣是不夠的,還需要用到Java中的元註解
元註解:
元註解是一種基本的註解,可以運用到註解上邊(可以理解為元註解也是一種標籤,只不過他是特殊的標籤,可以打在註解身上的一種註解)
Java中的註解有以下五種,@Retention,@Documented,@Target,@Inherited,@Repeatable
@Retention:保留,維持,保留的意思,該註解提供了註解的生命週期
取值有如下幾種:
RetentionPolicy.SOURCE: 標識該註解只保留原始碼階段,編譯器即忽視丟棄
RetentionPolicy.CLASS: 高註解保留到編譯階段,不回載入到jvm
RetentionPolicy.RUNTIME: 註解保留到執行時候
@Documented:標識該註解屬於文件型別,他的作用是將註解中的元素包含到javac中去
@Target:標識該註解運用的地方,限制註解的使用場景,target的取值如下:
ElementType.ANNOTATION_TYPE:可以給一個註解類進行註解
ElementType.CONSTRUCTOR:給一個構造方法進行註解
ElementType.FIELD:給屬性方法進行註解
ElementType.LOCAL_VARIABLE:給區域性方法進行註解
ElementType.METHOD:給方法進行註解
ElementType.PACKAGE:給包進行註解
ElementType.PARAMETER:給一個方法內的引數進行註解
ElementType.TYPE:給一個型別進行註解,型別包括:類,介面,列舉類
@Inherited:繼承註解,如果一個超類使用該註解,其子類沒有被任何註解標註的話,那麼這個子類就繼承了該超類的註解
@Repeatable:jdk1.8加進來的一個註解,意思是可重複的意思,使用該註解意味著註解的值可以取多個(這個註解我用到的很少,需要深入瞭解請百度)
註解屬性:
註解只有成員變數,沒有方法,成員變數就是屬性,例如:
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
public @interface Fruit {
int id();
String msg();
}
該註解定義了兩個成員:id和msg,在使用的時候,我們應該給它進行賦值
@Fruit(id = 1, msg = "anno")
public class TestAno {
}
在註解中定義的成員型別時候必須是8中基本資料型別+類,介面,註解,以及註解陣列
註解中的成員屬性還可以指定預設值
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
public @interface Fruit {
int id() default 0;
String msg() default "apple";
}
獲取註解:
我們使用註解的初衷就是對程式碼中的類,方法等打標籤,那它的作用是什麼,當然是在獲取類或者方法的時候,判斷這個類或者方法是不是該註解型別,進行特殊的操作和處理,那麼如何檢查和提取註解呢,相信你們已經想到,那就是使用Java中的反射機制
反射獲取註解:Class類中的方法判斷類中isAnnotationPresent()方法判斷是否運用註解,getAnnotation()方法獲取註解物件,getAnnotations()獲取所有的註解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return GenericDeclaration.super.isAnnotationPresent(annotationClass);
}
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
return (A) annotationData().annotations.get(annotationClass);
}
public Annotation[] getAnnotations() {}
如下利用反射獲取註解的例子:
@Fruit
public class Test {
public static void main(String[] args) {
boolean isAnno = Test.class.isAnnotationPresent(Fruit.class);
if(isAnno) {
Fruit annotation = Test.class.getAnnotation(Fruit.class);
System.out.println(annotation.id());
System.out.println(annotation.msg());
}
}
}
執行的結果當然是:0 apple
接下來看註解在方法的註解如何提取
1. 先定義一個屬性註解:MyParamAnno
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyParamAnno {
String value();
}
2. 定義一個方法型別註解:MyMethodAnno
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
public @interface MyMethodAnno {
}
3. 解析方法屬性上註解示例:
@Fruit(msg = "skyline")
public class MethodAnnoTest {
@MyParamAnno("hello")
String param;
@MyMethodAnno
public void method(){}
public static void main(String[] args) {
boolean isAnno = MethodAnnoTest.class.isAnnotationPresent(Fruit.class);
if (isAnno) {
Fruit annotation = MethodAnnoTest.class.getAnnotation(Fruit.class);
System.out.println(annotation.msg());
}
//解析屬性方法註解
try {
Field param = MethodAnnoTest.class.getDeclaredField("param");
param.setAccessible(true);
//獲取該變數的註解
MyParamAnno paramAnno = param.getAnnotation(MyParamAnno.class);
if(paramAnno != null) {
System.out.println("成員屬性的註解值為:" + paramAnno.value());
}
//獲取方法的註解
Method method = MethodAnnoTest.class.getDeclaredMethod("method");
if (method != null) {
MyMethodAnno methodAnno = method.getAnnotation(MyMethodAnno.class);
System.out.println("方法註解:" + methodAnno.annotationType().getSimpleName());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
輸出的結果值為:
skyline
成員屬性的註解值為:hello
方法註解:MyMethodAnno
總結:
註釋是為了標註和解釋程式碼,但是由於用到反射機制,所以會對效能方面有所影響,所以不要濫用。