Java注解
注解是JDK5引入的一个特性,可以为我们的代码打上一个标记,配合java中的反射会有意想不到的惊喜。我们经常用到很多框架都是用到了该特性。所有的注解都继承于Annotation。
Java 8以前,注解只能在声明的地方使用(声明方法、变量、类等),Java 8开始,注解可以在任何地方使用(泛型、抛异常、方法中等),这是因为Java 8 增加了两种ElementType(下文会介绍)。
- 备注:代码基于Android API 26 中的java代码以及Java 8中的代码(Mac>>Android studio 3.3.2)。
注解的示例
- 自定义注解代码示例:
/**
* 测试注解
* TestApplication
* Created by anonyper on 2019/5/30.
*/
@Retention(RetentionPolicy.RUNTIME)//属于运行时注解
@Target(ElementType.METHOD)//适用于方法注解
public @interface TestAnnotationMethod {
String LabelType() default "default";//注解中LabelType 取值
}
- 系统内置注解代码示例:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- 使用代码示例:
public class RachelApplication extends Application {
//系统注解
@Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent(this, LocationService.class);
startService(intent);
}
//自定义注解
@TestAnnotationMethod(LabelType = "Wifi")
public void testMethod() {
}
}
注解基本语法介绍
从示例中可以看到,使用@interface即可标示这是一个注解,一个注解包括元注解和注解属性两部分。下面我们分别来介绍元注解、Java内置的常用注解以及注解属性。
注解属性
注解起到一种标记作用,有的时候我们需要给定不同的属性,比如上面的String LabelType() default "default";//注解中LabelType 取值
这段代码。这是用来给一个注解指定一个属性。比如我们用到的@Target注解,里面的值是一个枚举,代表着该注解的不用使用范围。再比如,我们自定义一个注解用于标示人物身份,那么身份的值就可能包含:老师、学生、企业家、律师、医生等等。
注解的属性也可以叫做成员变量(注解内不能有方法的),定义属性的时候需要用无形参的方式来声明,并且方法名代表变量名字,返回值代表属性的类型(属性类型可为:8种基本类型(byte字节型、short短正型、int整型、long长整型、float单精度浮点型、double双精度浮点型、boolean布尔型、char字符型)、Class类型、String、枚举、注解以及他们的数组)。
- 属性的使用:
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Target(ElementType.TYPE)//适用于方法注解
public @interface TestAnnotation {
String LabelType() default "guanwang" ;
}
//使用:
@TestAnnotation(LabelType = "Wifi")
public class RachelApplication{
}
//或者
@TestAnnotation//默认是guanwang
public class RachelApplication{
}
//或者
@TestAnnotation()//默认是guanwang
public class RachelApplication{
}
- value
value算是一个特殊的属性,如果一个注解中只有value属性需要设置(没有其他属性或其他属性有默认值),在设置时value=可以省略。
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Target(ElementType.TYPE)//适用于方法注解
public @interface TestAnnotation {
String LabelType() default "guanwang" ;
String value();
}
//使用
@TestAnnotation("GPRS")
public class RachelApplication{
}
- 其他类型属性
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Target(ElementType.TYPE)//适用于方法注解
public @interface TestAnnotation {
int index() default 1;//int 类型
boolean flag() default false;//布尔类型
String LabelType() default "guanwang";//字符串
int[] arrayint() default {1, 2, 3, 4};//数组
ElementType elementType() default ElementType.TYPE;//枚举类型 用了ElementType做示例
Target target() default @Target(ElementType.TYPE); //注解类型 这里用了Target做示例
Class> classType() default Integer.class;//class类型
String value();
}
元注解
元注解就是可以标记注解的注解,我们在声明注解使用的范围@Target,生存周期@Retention等我们常用的,还有一些诸如@Documented、@Inherited、@Repeatable等元注解,我们接下来一一说明他们各自的含义:
@Target
限定注解的作用范围,比如方法、类、变量、等,具体如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Target {
ElementType[] value();
}
//ElementType 代码如下
public enum ElementType {
ANNOTATION_TYPE,//可以用在另一个注解上(自定义元注解)
CONSTRUCTOR,//可以用于构造方法上
FIELD,//可以用于字段声明上,包括枚举上
LOCAL_VARIABLE,//可以用于局部变量上
METHOD,//可以用于方法上
PACKAGE,//可以用于包声明上
PARAMETER,//可以用于参数声明上
TYPE,//可以用于类、接口、枚举声明上
TYPE_PARAMETER,//表示注解能写在类型变量的声明语句中。1.8版本加入的 网上的示例比如
//List list = new @Save ArrayList<>(); 但是我在测试过程中报错(Mac AndroidStudio 3.3.2 Android 26 java 1.8 ),唯一测试可用的地方:public class Book<@TestAnnotation T>
TYPE_USE;//表示注解能写在使用类型的任何语句中 1.8版本加入 如
/**
public class Book<@TestAnnotation T> {
List list = new @TestAnnotation ArrayList<>();
@TestAnnotation int index = 10;//默认10本书
public @TestAnnotation int getIndex() {
return 1;
}
}
*/
private ElementType() {
}
}
Java 8 新增的两个类型注解的作用是用来强健java代码,配合第三方工具(如Checker Framework)做强类型检查,可以在编译期检测出异常(如UnsupportedOperationException、NullPointerException异常等),避免了程序在运行时才抛出错误,这就是类型注解的主要作用。
@Retention
用来标示该注解的生存周期,比如运行时注解、编译时注解等。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Retention {
RetentionPolicy value();
}
//RetentionPolicy 代码如下
public enum RetentionPolicy {
CLASS,//标示注解只被保留到编译的时候,不会被加载到JVM中
RUNTIME,//标示注解可以保留到运行时,会被加载到JVM中
SOURCE;//标示注解只是在源代码阶段,在编译前会被忽略
private RetentionPolicy() {
}
}
@Documented
标示将注解的元素包含到javadoc中。(我们可以把我们的代码生成API文档的哦)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Documented {
}
@Inherited
标示改注解可以被继承,如果一个父类使用该注解,子类没有做其他注解时,默认继承了父类的注解。
@Inherited //该注解可以被继承
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Target(ElementType.TYPE)//适用于类注解
public @interface TestAnnotationType {
}
//父类使用注解,该注解有Inherited标示
@TestAnnotationType
class ParentClass {
}
//子类默认继承父类的注解
public class ChildClass extends ParentClass {
}
@Repeatable(Java 8增加的)
标示该注解在一个地方可重复使用,可以取不同的值,如一个人的身份可以是律师,他还兼职老师的工作。需要传入一个继承注解的类来作为其容器。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Repeatable {
Class extends Annotation> value();
}
//使用方法:
//注解容器
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Identitys {
Identity[] value();//必须有一个方法名为 value 返回的类型用的是使用Repeatable的注解
}
//声明可重复
@Repeatable(Identitys.class)//容器类是Identitys
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Identity {
String label();
}
//具体使用
@Identity(label = "student")
@Identity(label = "teacher")
public class TestRepeatable {
}
Java内置常用注解
我们在继承一个类,子类重新父类方法时是不是用到@Override,在看到老版本废弃的方法时,是否看到@Deprecated呢,这些都是Java内置的注解,常用的有:
@Override
标示子类复写了父类的方法用到的修饰。防止父类方法名改变时,子类方法忘记做对应调整。
@Deprecated
标示过时的元素,虽然可以调用,但不建议使用的意思,使用的时候,在IDE上会显示一条灰色的线。
@SuppressWarnings
阻止编译器对被标记方法、类发出警告,里面需要传入阻止警告的哪些类型,常用的:all(所有警告)、unchecked(没有进行类型检查操作的警告)、unused(没有使用过的代码警告),deprecation(使用过期方法)。所以在调用过时方法的方法前面加上@SuppressWarnings("deprecation"),那条灰色就不会显示了。
具体SuppressWarnings类型值的解释,可以自行百度。
@SafeVarargs (Java 7新增)
Java SE 5中引入的可变形参加上Java编译器的机制,导致在使用可变形参的时候,编译器会产生一个未经检查的警告,SafeVarargs注解就可以忽略这些警告。SafeVarargs注解只能用在参数长度可变的静态方法(或用final修饰)或构造方法上,否则会出现编译错误。(这些只是屏蔽了编译时的警告,如果类型转化错误,在运行时还会报错的)。
SafeVarargs产生由来介绍
@FunctionalInterface(Java 8新增)
该注解标示该类是一个函数式的接口,就是里面除了Object类中public修饰的方法外只有一个抽象方法。比如 View.OncClickListener,Runnable等都可以使用FunctionalInterface标记一下。这些函数式接口可以使用lambda表达式来编写。
注解的使用
上面介绍了注解的基本知识,我们学会了如何对一个元素用注解进行标记,那么接下来我们就要考虑标记之后如何识别呢?这些内容因为篇幅原因,我们就另开一篇分析。