Java注解

目录

简述

分类

JDK提供的注解

@Override

@Deprecated

@SuppressWarnings

@Retention

@Documented

@Target

@Inherited

@SafeVarargs

@FunctionalInterface

@Repeatable

自定义注解

注解声明

@interface

元注解

@Target 用于表示注解可应用的元素类型

@Retention 用于指定注解的存储方式(保留级别)

声明注解成员

通过反射获取注解信息

APT注解处理器Annotation Processing Tool

Android中的语法检查

根据不同保留级别的应用场景 

Android注解小应用(通过注解findViewById)

注解

反射获取信息进行处理

试用一下

简述

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响

然后说注解本质的操作机理 通过反射获取注解信息 根据注解信息作出处理

分类

JDK提供的注解 第三方框架的注解 自定义注解

JDK提供的注解

Java 定义了一套注解,目前共有 10 个。有三个是在Java7后加入的。此处仅统计至Java8。下面仅介绍注解的基本意义,具体常用注解的使用方式请往后看。

@Override

检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

@Deprecated

标记过时方法。如果使用该方法,会报编译警告。

@SuppressWarnings

指示编译器去忽略注解中声明的警告。

以下四个为元注解 能够标记注解的注解就是元注解 具体的使用请看后续元注解的使用

@Retention

标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。

@Documented

标记这些注解是否包含在用户文档中。

@Target

标记这个注解应该是哪种 Java 成员。

@Inherited

标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)。

Java7后加入

@SafeVarargs

忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

@FunctionalInterface

Java 8 开始支持,标识一个匿名函数或函数式接口。

@Repeatable

Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

自定义注解

注解声明

Java中所有的注解都默认实现Annotation接口

下面就是注解的关键字 比如类的关键字是class 注解的关键字是@interface 

@interface

 这就是一个注解类

public @interface PrintHello {

}

元注解

上面已经介绍过了 元注解就是可注解注解的注解类 就是给注解类用的注解 呃呃呃 好像有些拗口

常用的元注解有两种

@Target 用于表示注解可应用的元素类型

ElementType.TYPE 意味着,它能标注"类、接口(包括注释类型)或枚举声明"。

ElementType.FIELD 意味着,它能标注"字段声明"。

ElementType.METHOD 意味着,它能标注"方法"。

ElementType.PARAMETER 意味着,它能标注"参数"。

ElementType.CONSTRUCTOR 意味着,它能标注"构造方法"。

ElementType.LOCAL_VARIABLE 意味着,它能标注"局部变量"。

ElementType.ANNOTATION_TYPE 意味着,它能标注"注解"。

ElementType.PACKAGE 意味着,它能标注"包声明"。

那么被ElementType.TYPE标记的注解是不是就是元注解呢?是的!

@Target(ElementType.TYPE)

@Retention 用于指定注解的存储方式(保留级别)

RetentionPolicy.SOURCE  标记的注解仅保留在源级别中,并被编译器忽略

RetentionPolicy.CLASS      标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略

RetentionPolicy.RUNTIME   标记的注解由 JVM 保留,因此运行时环境可以使用它

@Target(ElementType.TYPE) // 注解级别 任意元素
@Retention(RetentionPolicy.CLASS) // 保留级别 保留至编译器 即.class中含有注解信息
public @interface PrintHello {

}

声明注解成员

定义的成员变量只能是String、数组、Class、枚举、注解等类型

@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) // 可以注解类、属性、方法
@Retention(RetentionPolicy.RUNTIME) // 使用反射获取标记信息 保留至运行时 
public @interface PrintHello {
    String value(); // value 在仅有一个的情况下可以不需要指明
    String name() default "猜猜我是谁"; // 可以设置默认值
}

给打印方法做个标记

    @PrintHello(value = "简单使用下" , name = "我叫我不猜") 
    private void print(){
        
    }

通过反射获取注解信息

public static void main(String[] args) {
        Class clazz = Test.class;
        try {
            // 取得方法信息
            Method method = clazz.getDeclaredMethod("print");
            // 获取方法上的注解标记
            PrintHello annotation = method.getAnnotation(PrintHello.class);
            String value = annotation.value();
            String name  = annotation.name();
            System.out.println(value+name);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

后面的案例主要以Android用例为主 仅了解注解无需向后阅读

APT注解处理器Annotation Processing Tool

注解处理器需要单独创建一个 Java Library 子模块来存放,我们创建一个名为 complier 的子模块。

然后在main下创建resources目录 继续创建META-INF.services目录

创建javax.annotation.processing.Processor文件 内容是注解处理器的全类名

比如  com.example.complier.PrintProcessor

目录如下

Java注解

 注解处理程序如下

package com.example.complier;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

// SupportedAnnotationTypes 指定注解处理器仅处理PrintHello
// Javac编译过程中调起 执行注解处理程序
@SupportedAnnotationTypes("com.example.annotationdemo.annotation.PrintHello")
public class PrintProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        Messager messager = processingEnv.getMessager();
        messager.printMessage(Diagnostic.Kind.NOTE,"注解处理程序");
        // 想干啥干啥
        // 多数场景是自动生成辅助类
        return false;
    }
}

比如上面代码就是打印了注解处理程序 绝大多数情况下注解处理程序用于生成辅助类

Android中的语法检查

Android包提供的一个语法检查元注解 @IntDef

新建一个日期检查注解WeekDay

package com.example.annotationdemo.annotation;


import androidx.annotation.IntDef;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@IntDef({1,2,3,4,5,6,7})
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
public @interface WeekDay {

}

比如给参数加个注解 那么在穿参时 非1234567外的整数会提示警告

    private void censor(@WeekDay int weekDay){

    }

根据不同保留级别的应用场景 

Java注解

Android注解小应用(通过注解findViewById)

注解

package com.example.annotationdemo.annotation;


import androidx.annotation.IdRes;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectView {
    @IdRes int value();
}

反射获取信息进行处理

package com.example.annotationdemo.inject;

import android.app.Activity;
import android.view.View;

import com.example.annotationdemo.annotation.InjectView;

import java.lang.reflect.Field;

public class InjectUtils {

    public static void injectView(Activity activity){
        Class<? extends Activity> aClass = activity.getClass();
        try {
            // 拿到类的所有属性
            Field[] declaredFields = aClass.getDeclaredFields();
            for(Field declaredField : declaredFields){
                // 判断是否被注解标记
                if (declaredField.isAnnotationPresent(InjectView.class)){
                    // 被注解标记获取注解信息
                    InjectView annotation = declaredField.getAnnotation(InjectView.class);
                    // 得到注解标记的值
                    int value = annotation.value();
                    View viewById = activity.findViewById(value);
                    // 允许操作
                    declaredField.setAccessible(true);
                    // 反射设置属性的值
                    declaredField.set(activity,viewById);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

试用一下 

package com.example.annotationdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import com.example.annotationdemo.annotation.InjectView;
import com.example.annotationdemo.inject.InjectUtils;


public class MainActivity extends AppCompatActivity {

    @InjectView(R.id.textview)
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectUtils.injectView(this);
        tv.setText("嘿咻");
    }


}
上一篇:程序猿和产品狗 (10 分)——友元函数的借用


下一篇:LocalStroage上手:实现一个刷新后仍然存在的ToDoList