什么是注解
官网上是这么介绍注解的:Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。
其实说白了,注解只不过是一种特殊的注释而已
作用分类
编写文档:通过代码里标识的元数据 (注解) 生成文档(比如 doc 文档) 代码分析:通过代码里标识的元数据 (注解) 对代码进行分析(反射) 编译检查:通过代码里标识的元数据 (注解) 让编译器能够实现基本的编译检查(例如:Override)
Java 提供的基本注解
@Override (限定父类重写方法)
@Override:当子类重写父类方法时,子类可以加上这个注解,那这有什么什么用?这可以确保子类确实重写了父类的方法,避免出现低级错误。
@Deprecated (标记已过时)
@Deprecated:这个注解用于表示某个程序元素类,方法等已过时,当其他程序使用已过时的类,方法时编译器会给出警告。
@SuppressWarnings (抑制编译器警告)
被该注解修饰的元素以及该元素的所有子元素取消显示编译器警告,例如修饰一个类,那他的字段,方法都是显示警告。
示例:
// 抑制编译器对该类以及类中方法的所有警告@SuppressWarnings("all")public class test(){// 抑制编译器对该方法的所有警告@SuppressWarnings("all")public static void test1(){}}
@SafeVarargs (抑制编译器 unchecked 警告)
在声明具有模糊类型(比如:泛型)的可变参数的构造函数或方法时,Java 编译器会报 unchecked 警告。鉴于这些情况,如果程序员断定声明的构造函数和方法的主体不会对其 varargs 参数执行潜在的不安全的操作,可使用 @SafeVarargs 进行标记,这样的话,Java 编译器就不会报 unchecked 警告。
@FunctionalInterface (函数式接口)
这个注解保证这个接口只有一个抽象方法,FunctionalInterface 只能修饰接口。
元注解
元注解其实就是描述注解的注解。
@Retention
Reteniton 注解的作用是:描述注解保留的时间范围。
Reteniton 注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在 RetentionPolicy 枚举中。
public enum RetentionPolicy {SOURCE, // 源文件保留CLASS, // 编译期保留,默认值RUNTIME // 运行期保留,可通过反射去获取注解信息}
@Target
Target 注解的作用是:描述注解的使用范围
Target 注解用来说明那些被它所注解的注解类可修饰的对象范围:注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch 参数),在定义注解类时使用了 @Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在 ElementType 枚举中。
public enum ElementType {TYPE, // 类、接口、枚举类FIELD, // 成员变量(包括:枚举常量)METHOD, // 成员方法PARAMETER, // 方法参数CONSTRUCTOR, // 构造方法LOCAL_VARIABLE, // 局部变量ANNOTATION_TYPE, // 注解类PACKAGE, // 可用于修饰:包TYPE_PARAMETER, // 类型参数,JDK 1.8 新增TYPE_USE // 使用类型的任何地方,JDK 1.8 新增}
@Documented
Documented 注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
@Inherited
Inherited 注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解)。
类型注解
在以前的 Java 版本中,开发者只能将注解(Annotation)写在声明中。对于 Java 8,注解可以写在使用类型的任何地方,例如声明、泛型和强制类型转换等语句,例如:
@Encrypted String data;List<@NonNull String> strings;myGraph = (@Immutable Graph) tmpGraph;
自定义注解
自定义注解语法
public @interface annotationName{}
注解中的属性
既然注解本质是一个接口,那么注解也拥有接口的特性,例如定义常量与方法,且在定义抽象方法时,其返回值须为以下几种:
基本数据类型 String 枚举 注解 以上类型的数组
例:
public @interface test {final String NAME = "zhangsan";String name() default="zhangsan";int age();ElementType elementType();SuppressWarnings suppressWarnings();String[] names();}
注意:
在使用注解时,需要给属性赋值,如果在定义时,设定了默认值,则可以忽略赋值操作,采取默认值。
@test(age=12 ...)如果在定义属性时,只有一个属性,且名字叫 value,则可以省略不写,直接定义值即可,如果有多个,则不可省略
例:
// 定义public @interface test {int value();}// 使用@test(12)
属性赋值示例:
@test(name = "zhangsan", //字符串age = 12, // 数值elementType= ElementType.TYPE, // 枚举suppressWarnings = @SuppressWarnings({}), // 注解names={"zhangsan","lisi"}) //数组,采用大括号分割,如果数组中只有一个值,大括号可省略不写
注解的本质
我们自定义一个名为 annotationName 的注解,生成 class 文件之后,反编译该文件:
public interface annotationName extends java.lang.annotation.Annotation {}
从上面我们可以看到,注解其实就是一个默认继承了 java.lang.annotation.Annotation
接口的一个接口。
注解的使用示例
定义三个注解
// 允许作用在类上@Target({ElementType.TYPE})// 运行期保留,可通过反射去获取注解信息@Retention(RetentionPolicy.RUNTIME)public @interface ClassAnnotation {String className();}
// 允许作用在方法上@Target({ElementType.METHOD})// 运行期保留,可通过反射去获取注解信息@Retention(RetentionPolicy.RUNTIME)public @interface MethodAnnotation {String methodName();}
// 允许作用在成员变量上@Target({ElementType.FIELD})// 运行期保留,可通过反射去获取注解信息@Retention(RetentionPolicy.RUNTIME)public @interface AttributesAnnotation {String attributesName();}
定义类并使用注解
@ClassAnnotation(className = "com.minio.demo.Test")public class Test {@AttributesAnnotation(attributesName = "zhangsan")public String name = "name";@MethodAnnotation(methodName = "show")public void show() {System.out.println("方法被执行...");}public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {// 获取被注解类的class对象ClasstestClass = Test.class; // 获取注解对象 此操作会在内存中生成一个该注解接口的子类实现对象ClassAnnotation classAnnotation = testClass.getAnnotation(ClassAnnotation.class);// 调用注解对象中对应的抽象方法,得到其设定的值String className = classAnnotation.className();System.out.println(className);// 获取定义的className的Class对象Class aClass = Class.forName(className);// 得到该类对象中的所有方法Method[] methods = aClass.getMethods();String methodName = "";String attributesName = "";for (Method method : methods) {// 判断该方法是否存在该注解// 采用判断isAnnotationPresent方法判断if (method.isAnnotationPresent(MethodAnnotation.class)) {// 如果存在,取出该注解MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);// 获取注解信息methodName = methodAnnotation.methodName();}}for (int i = 0; i < methods.length; ++i) {// 得到类方法中的注解MethodAnnotation methodAnnotation = methods[i].getAnnotation(MethodAnnotation.class);// 如果不等于null,则代表改方法存在注解// 采用if是否空判断法if (methodAnnotation != null) {// 获取注解信息methodName = methodAnnotation.methodName();}}// 执行对应的方法Method method = aClass.getMethod(methodName);// 创建该类对象Object obj = aClass.newInstance();// 执行方法method.invoke(obj);// 得到所有字段Field[] declaredFields = aClass.getDeclaredFields();for (int i = 0; i < declaredFields.length; i++) {AttributesAnnotation annotation = declaredFields[i].getAnnotation(AttributesAnnotation.class);attributesName = annotation.attributesName();}System.out.println(attributesName);}}
输出结果:
com.minio.demo.Test方法被执行...zhangsan




