Java註解(Annotation)詳解

Java註解(Annotation)詳解

1.Annotation的概念

An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.java

  • 註解是一種能夠添加到Java源代碼的元數據.
  • 類,方法,變量,參數,包均可以被註解.
  • 註解對註解的代碼並無直接的影響.
  • 註解僅僅是個標記.註解之因此起做用是對其解析後作了相應的處理

2.Annotation分類

  • 標準Annotation
    • 標準Annotation是指Java內置的三個Annnotaion:
    • @Override:用於修飾此方法覆蓋了父類的方法.
    • @Deprecated:用於修飾已通過時的方法.
    • @SuppressWarnnings:用於通知java編譯器禁止特定的編譯警告.
  • 元Annotation(註解的註解)
    • 元Annotation是用來定義Annotation的Annotation
    • 元Annotation能夠定義Annotation的做用範圍,使用在什麼元素上等
    • 元註解共有四種@Retention, @Target, @Inherited, @Documented
  • 自定義Annotation

3.元Annotation

  • @Retention:注在其餘的註解A上,用來講明A的保留範圍,可選值 SOURCE(源碼時),CLASS(編譯時),RUNTIME(運行時),默認爲 CLASS
    • SOURCE:A只保留在源碼中,A會被編譯期忽略.(源碼可用)
    • CLASS:A會經過編譯保存在CLASS文件中,但會被JVM在運行時忽略,運行時不可見.(源碼+CLASS可用)
    • RUNTIME:A會被JVM獲取,並在運行時經過反射獲取.(源碼+CLASS+運行時都可用)
  • @Target:注在其餘的註解A上,用來限制A可用修飾那些程序元素.未標註Target表示無限制,可修飾全部元素.
    • ANNOTATION_TYPE: A能夠應用到其餘註解上
    • CONSTRUCTOR: A可使用到構造器上
    • FIELD: A可使用到域或屬性上
    • LOCAL_VARIABLE: A可使用到局部變量上。
    • METHOD: A可使用到方法上。
    • PACKAGE: A可使用到包聲明上。
    • PARAMETER: A可使用到方法的參數上
    • TYPE: A可使用到類,接口(包括註解),或枚舉的聲明上
  • @Inherited:默認狀況下,父類的註解不會被子類繼承.
    • Inherited注在其餘的註解A上.
    • 只有當A是註解在類Class上面,Inherited纔會起做用,其餘任何狀況下無效果.
    • 當A註解在類C上面,則C的全部子孫類,都會繼承應用A註解;
  • @Documented:注在其餘的註解A上,A將會做爲Javadoc產生的文檔中的內容。註解都默認不會成爲成爲文檔中的內容。

4.自定義Annotation

  1. 建立自定義Annotation流程android

    • public @interface 自定義註解名稱
      public @interface CustomAnnotation{***}
          
      複製代碼
    • 設置自定義Annotation的保留範圍和目標,Retention和Target是最重要的兩個元Anitation.
      @Retention( RetentionPolicy.RUNTIME )
      @Target( ElementType.TYPE )
      public @interface CustomAnnotation{***}
      複製代碼
    • 設置自定義Annotation的註解參數(註解成員)
      • 註解參數支持的數據類型
        • 全部基本數據類型(int,float,boolean,byte,double,char,long,short)
        • String類型
        • Class類型
        • enum類型
        • Annotation類型
        • 以上全部類型的一維數組
      • 註解參數聲明方式
        @Retention( RetentionPolicy.RUNTIME )
        @Target( ElementType.TYPE )
        public @interface CustomAnnotation{
            //註解參數類型能夠是1-6中任一種,包括枚舉
            public enum Skill{JAVA,ANDROID,IOS}
            Skill mySkill() default Skill.ANDROID;
            String attr1();
            //可使用default設置默認值
            int attr2() default 100;
            //修飾符只能用public
            public boolean attr3() default false;
        }
        @Retention( RetentionPolicy.RUNTIME )
        @Target( ElementType.TYPE )
        public @interface CustomAnnotation{
            //只有一個註解參數,使用value()
            String value();
        }
        複製代碼
        • 自定義Annotation的參數類型必須知足上一條1到6中的範圍.
        • 自定義Annotation的參數訪問方法只能是public,或不寫.
        • 自定義Annotation的參數能夠加 default 設置默認值.
        • 自定義Annotation若只有1個參數,使用value().
  2. 自定義Annotation的註解參數的默認值數組

    註解元素必須有肯定的值,要麼在定義註解的默認值中指定,要麼在使用註解時指定,非基本類型的註解元素的值不可爲null。所以, 使用空字符串或0做爲默認值是一種經常使用的作法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,由於每一個註解的聲明中,全部元素都存在,而且都具備相應的值,爲了繞開這個約束,咱們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義註解時,這已經成爲一個習慣用法。bash

    示例:
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AnotherAnnotation{
        String author() default "";
        int age() default -1;
    }
    複製代碼
  3. 使用剛剛建立的自定義註解app

    @CustomAnnotation(attr1 = "屬性1", attr2 = 90, attr3 = true)
    public class AnnotationTestClass{
        ***
    }
    複製代碼

5.Annotation解析

  • 運行時 Annotation 解析框架

    運行時 Annotation 指 @Retention 爲 RUNTIME 的 Annotationide

    • Class,Method,Field中都有如下3個方法能夠調用
      • public T getAnnotation(Class annotationClass) 按照傳入的參數獲取指定類型的註解。返回null說明當前元素不帶有此註解。
      • public final boolean isAnnotationPresent(Class<? extends Annotation> annotationType) 檢查傳入的註解是否存在於當前元素。
      • public Annotation[] getAnnotations() 返回該元素的全部註解,包括沒有顯式定義該元素上的註解。
    • 運行時 Annotation 解析示例
      public void testCustomAnnotation() {
          try {
              Class cls = Class.forName("com.jet.annotation.AnnotationTestClass");
              CustomAnnotation customAnnotation = (CustomAnnotation)cls.getAnnotation(CustomAnnotation.class);
              System.out.println("customAnnotation mySkill:" + cus.mySkill());
              System.out.println("customAnnotation attr1:" + cus.attr1());
              System.out.println("customAnnotation attr2:" + cus.attr2());
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          }
      }
      複製代碼
  • 編譯時 Annotation 解析工具

    編譯時 Annotation 指 @Retention 爲 CLASS 的 Annotation,甴編譯器自動解析性能


6.編譯時Annotation解析

編譯時Annotation解析 相對複雜,下面單獨進行分析ui

首先申明:下面內容僅僅討論 編譯時Annotation的解析

  1. 編譯時Annotation的解析,是由Annotation Processor完成
  2. Annotation Processor(註解處理器)
    • 註解處理器是一個在javac中的,用來在編譯時掃描和處理註解的工具
    • 咱們能夠爲特定的註解,註冊自定義的註解處理器
    • 在編譯期間,JVM會自動運行註冊過的註解處理器
    • 一個註解的Annotation Processor,以Java代碼(或者編譯過的class)爲輸入,生成.java文件做爲輸出.這意味着咱們能夠生成新的Java代碼!這些生成的Java代碼是在生成的.java文件中,新生成的.java文件會和普通的手動編寫的Java源代碼同樣被javac編譯
  3. 每個註解處理器都是繼承於AbstractProcessor,須要關注的有如下4個方法
public abstract class AbstractProcessor implements Processor {

    //對一些工具進行初始化
    public synchronized void init(ProcessingEnvironment processingEnv)
    
    //你在這裏定義你的註解處理器註冊到哪些註解上,必須指定;
    //它的返回值是一個字符串的集合,包含本處理器想要處理的註解類型的合法全稱
    public Set<String> getSupportedAnnotationTypes()
    
    //指定該註解處理器使用的JAVA版本,一般返回SourceVersion.latestSupported()
    public SourceVersion getSupportedSourceVersion()
    
    //真正生成java代碼的地方
    //annotations:請求處理的註解類型集合
    //roundEnv:可讓你查詢出包含特定註解的被註解元素,至關於「有關全局源碼的上下文環境」
    //若是返回 true,則這些註解已聲明而且不要求後續 Processor 處理它們;
    //若是返回 false,則這些註解未聲明而且可能要求後續 Processor 處理它們
    public abstract boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv)
    
}
複製代碼
  1. 自定義註解處理器,就是繼承AbstractProcessor並重寫上述4個方法

關於編譯時Annotation解析,這裏推薦一篇文章【Android】註解框架(三)-- 編譯時註解,手寫ButterKnife,按照文章上面流程敲一遍代碼,相信能夠對自定義註解的建立及解析有一個深刻的瞭解!

7.註解對App的影響

  1. 運行時註解對性能有影響,編譯時註解對App的性能沒有影響.
  2. 運行時註解的解析徹底依賴於反射,反射的效率比直接調用慢,只有過多使用運行時註解時纔對效率有必定影響
  3. java文件編譯成.class文件。再對class文件進行打包等一系列處理。生成apk。最終才運行到咱們的手機上。而編譯時註解,就是在java編譯生成.class文件這一步進行的操做。根本和咱們的apk運行無關,不存在影響性能的問題;
  4. 編譯時註解庫,在app的引用通常以下:
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    //API模塊:android library
    implementation project(':butterknife')
    //Annotation模塊:java library
    implementation project(':butterknife-annotations')
    //Annotation解析模塊:java library
    annotationProcessor project(':butterknife-compiler')
}
複製代碼

實際在打包生成APK的過程當中,只有 API模塊和Annotation模塊 會被打包進APK,Annotation解析模塊是提供給IDE使用的,在咱們APK中並不存在.

相關文章
相關標籤/搜索