1. 程式人生 > 實用技巧 >Java的註解淺析

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

總結:

  註釋是為了標註和解釋程式碼,但是由於用到反射機制,所以會對效能方面有所影響,所以不要濫用。