1、什么是Java注解
Java Annotation(注解):
从JDK1.5引入
Annotations用于提供有关程序的补充信息,位于源码中的
Annotation以"@"开始
Annotation不会改变已经编译程序的动作
Annotation帮助关联metadata(信息)到程序的元素(用来修饰程序的元素,但不会对被修饰的对象有直接影响,通过其他的工具对标签进行处理(只有通过某种配套的工具才会对注解信息进行访问和处理)):instance variables, constructors, methods, classs等等。
其他工具:编译器、IDE
Annotation are not pure comments,因为它可以改变编译器处理程序的方式,但是它没有起到直接的作用,知识起到一个信息提示的作用。
Annotations的用途:
1.提供信息给编译器/IDE
2. 可用于其他的工具产生额外代码/配置文件等
3.有一些注解可以在程序运行时访问,增加程序的动态性,即在程序运行时,通过反射的方法来访问注解,使得程序可以增加额外的功能。
@SuppressWarnings:压制警告
@Deprecated:丢弃
@Override:其实即使一个Method与父类方法名同名的话,也会覆盖(不用Override注解)
2、Annotations分类
1.Marker Annotations
这种类型的Annotatiosn的唯一目的是mark一个声明。这些注解不包含任何成员,也不包含任何数据。因此,它作为一个注解的存在就足够了。由于marker interface不包含任何成员,所以简单地确定它是存在还是不存在就够了,@Override就是一个marker Annotation.
@TestAnnotation
2.Single Value Annotations
该类型注解只包含一个成员,并允许一种指定成员值的shorthand形式。我们只需要在应用注解时为该成员赋值就行,而不需要指定成员的名称。但是,要使用此简写,成员的名称必须是一个值。
@TestAnnotation("testing")
3. Fully Annotations
该注解包含多个数据成员,名字,值对。
@TestAnnotation(key1 = "1", key2 = "2")
4. Type Annotations
该注解可以应用于正在使用的类型的任何地方。例如,我们可以注解一个方法的返回值类型。这些申明都是用@Target进行注解
5. Repeating Annotations
该注解是可以多次应用于单个项的Annotation。要使得Annotation可以重复,必须使用@Repeatable注解,它的值字段制定了container type for the repeatable annotation.container被指定为一个Annotations,它的值字段是可重复注解类型的数组。因此,创建一个repeatable annotation,首先创建一个container注解,然后注解type被指定为一个参数给@Repeatable注解。
3、Predefined/Standard Annotations
Java定义了7个内置注解(用来修饰Java代码的)。
1.四个来自于java.lang.annotation:@Retention、@Documented、@Target、@Inherited、Repeatable(元注解:修饰注解的注解)
2.三个来自于java.lang:@Deprecated、@Override、SuppressWarnings
Annotation1: @Override
它是一个只能用于方法的marker annotation.使用@Override注解的方法必须覆盖父类中的方法,如果没有将导致编译错误。它用于确保父类的方法实际被重载,而不是简单的重载。
Annotation2: @Deprecated
1、该注解是一个marker annotation.它指示改声明已经过时,并且已经被较为新的形式所取代。建议程序员不再使用该类/元素/方法.
2、javadoc @deprecated tag,当一个元素被deprecated时,应该被使用
3、@deprecated tag是用用于文档,@Deprecated注解是运行时反射
4、当两者都使用时,@deprecated tag比@Deprecated 注解有更高的优先级
Annotation3: @SupressWarnings
该注解用于通知编译器以抑制指定的编译器警告。要抑制的警告以字符串的形式指定。该类型的注解可以用于任何类型的声明(变量/方法/构造函数/类等)。
Java将Warnings分成两类,一个是deprecated,一个是unchecked,当legacy代码接口和使用泛型的代码unchecked警告会生成
SupressWarnings("unchecked")、 SupressWarnings("deprecated")、 SupressWarnings("all")、
Annotation4: @Target
该注解被设计仅用于作为另一个注解的注解,@Target接收一个参数,该参数必须是ElementType枚举中的一个常量。此参数指定可以应用注解的声明的类型。我们可以在@Target注解中指定一个或者多个值。如果要指定多个值,我们必须在大括号分割的列表中指定它们。例如,要指定注释适用于字段和局部变量,可以使用如下:@Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE})
Annotation5: @Retention
该注解决定了注解被保留的位置以及多长时间(用来修饰其他注解的存在范围),决定被修饰的注解将来保存在什么地方。它有RetentionPolicy三个值:
SOURCE:注释将被保留在源代码级别(.java文件),并被编译器忽视,也就是不在.class文件
CLASS:这时默认的注解保留策略,注释将在编译时保留,并被JVM忽略(注解存在于.class文件中,但是不能JVM加载)
RUNTIME:这些数据将在运行时被保留,注解可以被JVM运行时访问到。通常情况下,可以通过反射技术对注解的字节码进行解析。
Annotation6: @Document
该注解是一个marker interface,它告诉一个工具要记录一个annotaion.Annotations不包括在"javadoc"注释中。在代码中使用@Documented,使得javadoc这样的工具能够处理解析,并在生成的文档中包含annotation type信息(帮助文档),就是让注解被Javadoc所识别。
Annotation7:@Inherited
该注解是一个marker annotation,只能够在annotation声明的时候使用。它只影响将用于类声明的Annotation。@Inherited会导致父类的annotation将被子类继承。因此,当向该子类发出对特定注解的请求时,如果该注解不在该子类上面,父类将会被checked。如果父类别注解修饰,并且用@Inherited修饰,然后这个注解将被返回。
import java.lang.annotation.Inherited
@Inherited
public @interface MyAnnotation{
}
@MyAnnotation
public class MySuperClass{
}
public class MySubClass extends MySuperClass{
}
Annotation8: @Repeatable
-- JDK1.8引入 --表示被修饰的Annotation可以重复应用标注,即这个注解可以重复用在被修饰的对象上
-- 需要定义注解和容器注解
// 定义注解
public @interface RepeatableAnnotation{
int a() default 0;
int b() default 0;
int c() default 0;
}
// 定义容器注解,用来存储多行重复的注解
public @interface RepeatbleAnnotations{
RepeatableAnnotation[] value(); // 这里叫做容器注解,存放多个需要定义的注解
}
@RepeatableAnnotation(a=1,b=2,c=3)
@RepeatableAnnotation(a=1,b=2,c=4)
public static void add(int a, int b, int c) // 这里其实相当于测试add方法,将测试用例写在注解上,可以边写程序,边完成测试用例
{
if (c != a + b){
throw new ArithmeticException("Wrong");
}
}
4、User-defined(custom) annotation
用户自定义注解可以用来注解程序元素(变量,构造函数,方法等),该注解可以用在声明元素之前
声明注解:
[Access Specifier] @interface <AnnotationName>
{
DataType <Method Name>() [default value]; // 这里其实是属性,只不过定义成方法的形式(只是没有参数),这时Java规定
DataType <Method Name>() [default value];
}
1、方法的返回值有:8 primitive type(char/short/int/float/double/long/byte/boolean)、String、enum、Class、注解类型、前面组成的数组
目录
3、Predefined/Standard Annotations
4、User-defined(custom) annotation
针对三种不同的加载策略有不同的解析方法
1.RetentionPolicy.RUNTIME:注解在class文件中,被JVM加载,可以反射解析注解
Class.getAnnotations(); // 获取这个类的头部有哪些Annotation修饰
Class.isAnnotation(); //这个类是否有注解修饰
// Method, Field, Constructor都有相应的API来获取上述成员
2. RetentionPolicy.CLASS:注解在class中,但是JVM没有加载,所以在程序运行时,我们是访问不到这部分的注解字节码的
这部分注解只能采用字节码工具进行特殊处理
如ASM工具, https://asm.ow2.io/
3. RetentionPolicy.SOURCE:注解在java文件中,,编译之后,不在class文件中,也不会被JVM加载,所以在程序运行中,访问不到这一部分源码级别的注解。
-- 只有在源码级别上进行注解处理
-- Java提供了注解处理器来解析带注解的源码,产生新文件
-- 注解处理器继承自AbstractProcessor,重写process方法
-- javac (Java编译工具:将.java编译成.class文件),编译的时候加上一个 -processor参数,然后把我们自己写的注解处理器写上去(可以写多个注解处理器,这些注解处理器会依次处理浙西源码级别的注解)javac -processor Processor1 Processor,.., sourceJavaFile
-- 编译器定位源文件的注解,然后依次启动注解处理器依次执行处理。如果某个注解处理器产生新的源文件(什么叫做新的源文件?因为注解处理器在读取源文件里面的源码级别的注解的时候,它会读取这些注解,然后会有新的文件出来,因为老的Java文件,是不能修改的),那么将重复执行这个处理过程
-- 注解处理器只能产生新的文件,不会修改已有的源文件,会读取这部分源码注解,然后,产生一个新文件,把新文件和老文件放在一起,继续编译,直到没有新的文件产生。