目录
简述
分类
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提供的注解 第三方框架的注解 自定义注解
Java 定义了一套注解,目前共有 10 个。有三个是在Java7后加入的。此处仅统计至Java8。下面仅介绍注解的基本意义,具体常用注解的使用方式请往后看。
检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
标记过时方法。如果使用该方法,会报编译警告。
指示编译器去忽略注解中声明的警告。
以下四个为元注解 能够标记注解的注解就是元注解 具体的使用请看后续元注解的使用
标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
标记这些注解是否包含在用户文档中。
标记这个注解应该是哪种 Java 成员。
标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)。
Java7后加入
忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
Java 8 开始支持,标识一个匿名函数或函数式接口。
Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
Java中所有的注解都默认实现Annotation接口
下面就是注解的关键字 比如类的关键字是class 注解的关键字是@interface
@interface
这就是一个注解类
public @interface PrintHello { }
上面已经介绍过了 元注解就是可注解注解的注解类 就是给注解类用的注解 呃呃呃 好像有些拗口
常用的元注解有两种
ElementType.TYPE 意味着,它能标注"类、接口(包括注释类型)或枚举声明"。
ElementType.FIELD 意味着,它能标注"字段声明"。
ElementType.METHOD 意味着,它能标注"方法"。
ElementType.PARAMETER 意味着,它能标注"参数"。
ElementType.CONSTRUCTOR 意味着,它能标注"构造方法"。
ElementType.LOCAL_VARIABLE 意味着,它能标注"局部变量"。
ElementType.ANNOTATION_TYPE 意味着,它能标注"注解"。
ElementType.PACKAGE 意味着,它能标注"包声明"。
那么被ElementType.TYPE标记的注解是不是就是元注解呢?是的!
@Target(ElementType.TYPE)
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用例为主 仅了解注解无需向后阅读
注解处理器需要单独创建一个 Java Library 子模块来存放,我们创建一个名为 complier 的子模块。
然后在main下创建resources目录 继续创建META-INF.services目录
创建javax.annotation.processing.Processor文件 内容是注解处理器的全类名
比如 com.example.complier.PrintProcessor
目录如下
注解处理程序如下
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包提供的一个语法检查元注解 @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){ }
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("嘿咻"); } }