注解语法
注解的定义
public @interface Text {
}
上方就是一个简单的注解,通过@interface 进行定义,他的定义跟接口很相似,比接口多了一个@符号,上方代码表示定义了一个Text的注解
注解的应用
@Text
public class Annotation {
public static void main(String[] args) {
}
}
直接在一个类上加上@Text 就可以使用,不过想要注解正常工作,需要理解元注解
元注解
元注解是可以注解到注解上的注解,元注解是一种基本注解,他可以应用到其他注解上
元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。
@Retention
当@Retention应用到注解上的时候,他解释了注解的存活时间
他的取值如下:
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
下面这段代码指定了运行期间可以获取,所以他的生命周期很长
@Retention(RetentionPolicy.RUNTIME)
@interface Text {
}
@Documented
这个元注解和文档有关,他的作用是可以把元注解中的元素包含到JavaDoc中
@Target
他指定了注解运用的地方,如果没有被@Target注解,那么他可以注解到任何地方,一旦一个注解被@Target注解后,这个注解就限定了应用场景,比如:
ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.PACKAGE 可以给一个包进行注解
ElementType.PARAMETER 可以给一个方法内的参数进行注解
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
@Inherited
这个表示继承,但是注解本身是不可以继承的,如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {}
@Text 注解被Inherited注解,A被@Text注解,B继承自A,但是B没有注解,B可以继承A的注解@Text
@Repeatable
@Repeatable是1.8之后加入的,是可重复的意思
@interface Persons {
Person[] value();
}
@Repeatable(Persons.class)
@interface Person{
String role default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}
@Repeatable注解了Person,@Repeatable后面括号中的类相当于一个容器注解
什么是容器注解?就是存放其他注解的地方,它本身也是一个注解
@interface Persons {
Person[] value();
}
按照规定他,他必须有一个value属性,属性值是,被@Repeatable注解过的注解数组
注解的属性
也叫做注解的成员变量,注解只有成员变量,没有方法,注解的成员变量,在注解中以“无参方法”来声明,其方法名定义了变量的名字,其返回值定义了变量的类型
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@interface Text {
int id();
String message();
}
这个注解有俩个属性id和message,在使用的时候我们应该给他们赋值
@Text(id = 1, message = "aa")
public class Annotation {
public static void main(String[] args) {
}
}
注解中定义属性的类型
所有基本类型(int,float,boolean,byte,double,char,long,short)
String
Class
enum
Annotation
上述类型的数组
注解中可以有默认值,默认值需要用default关键字定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@interface Text {
int id() default -1;
String message() default "";
}
因为又默认值的存在,那么注解可以不用赋值,直接用默认值
@Text
public class Annotation {
public static void main(String[] args) {
}
}
快捷方式
当注解中只有一个value属性时,应用这个注解时,可以直接把属性写在括号里
@interface Check {
String value();
}
@Check("q")
public class A {
}
当一个注解没有任何属性的时候括号都可以省略
注解属性必须要有确定的值,如果没有默认值,那么必须赋值,不管默认值或者赋值,都不能用null来赋值
Java的预制注解
java 本身已经实现的注解
@Deprecated
这个注解是用来标记已经过时的元素的,我们平时开发中应该总会遇到这种情况,提示我们正在使用过时的方法
public class Men {
@Deprecated
public void say(){
}
public void speak(){
}
}
@Override
用于标明此方法覆盖了父类的方法
@SuppressWarnings
用于有选择的关闭,编译器对类,变量的警告
其源码
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
其内部有个String 数组接受如下的值
deprecation:使用了不赞成使用的类或方法时的警告;
unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
path:在类路径、源文件路径等中有不存在的路径时的警告;
serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
finally:任何 finally 子句不能正常完成时的警告;
all:关于以上所有情况的警告。
注解的提取
到现在你已经会了自定义注解,也可以在类中使用自定义的注解,不过现在的注解,跟注释没有太大区别,没有太大的作用,怎么才能让注解工作起来呢?这就离不开反射
注解和反射
我们反编译注解类,发现他其实是继承自Annotation接口,也就是说java 用Annotation接口代表注解,Annotation是所有注解的父类
public interface DBTable extends Annotation{
public abstract String name();
}
为了运行时获取注解的信息,java 在反射包中增加了AnnotatedElement接口,它主要用于表示正在JVM运行的程序中已使用的注解元素,通过该接口提供的方法,可以利用反射技术提取注解的信息,如反射包的Constructor类、Field类、Method类、Package类和Class类都实现了AnnotatedElement接口,它简要含义如下
Class:类的Class对象定义
Constructor:代表类的构造器定义
Field:代表类的成员变量定义
Method:代表类的方法定义
Package:代表类的包定义
返回值 | 方法名称 | 说明 |
---|---|---|
< A extends Annotation> | getAnnotation(Class< A> annotationClass) | 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null。 |
Annotation[] | getAnnotations() | 返回此元素上存在的所有注解,包括从父类继承的 |
boolean | isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。 |
Annotation[] | getDeclaredAnnotations() | 返回直接存在于此元素上的所有注解,注意,不包括父类的注解,调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响,没有则返回长度为0的数组 |
@Text(id = 11, message = "22")
public class C {
@Check("33")
public int a;
@Text1(id = 44, message = "55")
public void f() {
}
public static void main(String[] args) {
Class<C> cClass = C.class;
boolean annotationPresent = cClass.isAnnotationPresent(Text.class);
if (annotationPresent) {
Text annotation = cClass.getAnnotation(Text.class);
System.out.println("获取类的注解" + annotation.message() + "/" + annotation.id());
}
try {
Field a = cClass.getField("a");
//获取a上的注解
Check check = a.getAnnotation(Check.class);
if (check != null) {
System.out.println("获取字段上的注解" + check.value());
}
Method f = cClass.getMethod("f");
Text1 text1 = f.getAnnotation(Text1.class);
if (text1 != null) {
System.out.println("获取方法上的注解" + text1.message() + "/" + text1.id());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
获取类的注解22/11
获取字段上的注解33
获取方法上的注解55/44
注解的使用场景
那么注解到底有什么用呢?
看下官方的解释
注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。
注解有许多用处,主要用处如下,
1 提供信息给编译器:编译器可以利用注解来探测错误和警告信息
2 编译阶段时的处理:软件工具可以用注解信息生成代码,HTML文档,或者其他相应的处理
3 运行时的处理:某些注解可以运行时接受代码提取
当开发者用注解修饰了类,变量,方法,这些注解不会自己生效,必须?开发者提供相应的代码处理注解,这些提取和处理注解的代码统称为,注解处理器
注解应用实例
ButterKnife,Dagger2,Retrofit,EventBus,使用了注解
参考:https://blog.csdn.net/briblue/article/details/73824058
https://blog.csdn.net/javazejian/article/details/71860633
Java编程思想