1.使用IntDef代替枚举

枚举一直不被Android中推荐使用.因为枚举中的每一个值在枚举类中都是一个对象.使用枚举值会比整型消耗的更多的内存.但有时我们又要限制取某些特定的值.intDef和StringDef两个魔术变量注解来替换枚举.厉害了,我的哥.

步骤:

  1. 声明一些必要的int(或者float,String等)常量
  2. 声明一个注解ThemeStyle
  3. 使用@intDef修饰ThemeStyle,参数设置为待枚举的集合
  4. 使用@Retention(Retention.SOURCE)指定注解仅存在于源码中,不加入到class文件中

具体使用:

public static final int STYLE_V1 = 0; // 单排排版
public static final int STYLE_V2 = 1; // 双排排版
 @IntDef({STYLE_V1,STYLE_V2})
 @Retention(RetentionPolicy.SOURCE)
 public @interface ThemeStyle{
 }
 //这样外部调用的时候只能传ThemeStyle.STYLE_V1,或者ThemeStyle.STYLE_V2.否则编译报错
  public static void setStyle(Context context, @ThemeStyle int style) {
   ...
 }

2.几种Threading注解

@UiThread UI线程

@MainThread 主线程

@WorkerThread 子线程

@BinderThread 绑定线程

3.Value Constraints注解: @Size,@IntRange@FloatRange

private void test(@IntRange(from=0,to=100)int a){
}
//举例
@Size(min=1)集合长度最小为1,即不能为空
@Size(max=100)String最大只能有100个字符
@size(2)数组只能有两个元素

4.@Permissions注解:@RequiresPermission

  • 单一权限
@RequiresPermission(Manifest.permission.INTERNET)
public void loadData(){
  ...
}
  • 需要任意一个
@RequiresPermission(anyOf={Manifest.permission.INTERNET,...})
public void loadData(){
  ...
}
  • 同时需要多个
@RequiresPermission(allOf={Manifest.permission.INTERNET,...})
public void loadData(){
  ...
}

5.@CallSuper

允许子类重写的方法,但是又需要在重写的时候调用自己的方法.为避免子类完全重写不调用自己的方法可用这个注解

@CallSuper
protected void onCreate(@nullable){
 doSoming()
}

6.@Keep

指出一个方法在被混淆时被保留

7.@VisibleForTesting

注解一个类,方法,变量表示有更广的可见性,使代码可以被测试

注解

@Retention、@Documented、@Target、@Inherited、@Repeatable

  • @Retention
  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

  • @Target
  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解

  • ElementType.CONSTRUCTOR 可以给构造方法进行注解

  • ElementType.FIELD 可以给属性进行注解

  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解

  • ElementType.METHOD 可以给方法进行注解

  • ElementType.PACKAGE 可以给一个包进行注解

  • ElementType.PARAMETER 可以给一个方法内的参数进行注解

  • ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

  • 注解的属性也叫做成员变量。注解只有成员变量,没有方法。

    注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnnotation {
    
        public int id() default -1;
    
        public String msg() default "Hi";
    
    }
    
  • 如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内。

    @Check("hi")
    int a;
    
  • 是一个注解没有任何属性。比如

    public @interface Perform {}
    

    那么在应用这个注解的时候,括号都可以省略。

    @Perform
    public void testMethod(){}
    

java预置的注解

  • @Deprecated

    过时的方法、过时的类、过时的成员变量

  • @Override

  • @SuppressWarnings

    阻止警告的意思

    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }
    
  • FunctionalInterface

    函数式接口注解.是一个具有一个方法的普通接口

注解与反射

  1. 注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
    
  2. 然后通过 getAnnotation() 方法来获取 Annotation 对象

    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
    

    或者是getAnnotations() 方法

    public Annotation[] getAnnotations() {}
    

    前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解

  3. 如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了

    @TestAnnotation
    public class Test {
        @Check("hi")
        int a;
    
        @Perform
        public void testMethod() {
        }
    
        @SuppressWarnings("deprecation")
        public void test1() {
            Hero hero = new Hero();
            hero.say();
            hero.speak();
    
        }
    
        public static void main(String[] args) {
            boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
            if (hasAnnotation) {
                //获取类的注解
                TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
                System.out.println("id:" + testAnnotation.id());
                System.out.println("msg:" + testAnnotation.msg());
            }
            try {
                Field a = Test.class.getDeclaredField("a");
                a.setAccessible(true);
                //获取一个成员变量上的注解
                Check check = a.getAnnotation(Check.class);
                if (check != null) {
                    System.out.println("check value:" + check.value());
                }
    
                Method testMethod = Test.class.getDeclaredMethod("testMethod");
                if (testMethod != null) {
                    // 获取方法中的注解
                    Annotation[] nas = testMethod.getAnnotations();
                    for (int i = 0; i < nas.length; i++) {
                        System.out.println("method testMethod annotation:" + nas[i].annotationType().getSimpleName());
                    }
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }
    
  4. 如果一个注解要在运行时被成功提取,那么 @Retention(RetentionPolicy.RUNTIME) 是必须的

使用场景

  • 当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。

总结

  1. 如果注解难于理解,你就把它类同于标签,标签为了解释事物,注解为了解释代码。
  2. 注解的基本语法,创建如同接口,但是多了个 @ 符号。
  3. 注解的元注解。
  4. 注解的属性。
  5. 注解主要给编译器及工具类型的软件用的。
  6. 注解的提取需要借助于 Java 的反射技术,反射比较慢慢,所以注解使用时也需要谨慎计较时间成本

保持求知欲,保持赤子心(*╯3╰)mua.