注解
一、注解的作用
1、编写文档:通过代码里标识的元数据生成文档。
2、代码分析:通过代码里标识的元数据对代码进行分析。
3、编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查。
二、基本内置注解
1、@Override
1.1、实现编译时检查,你可以为你的方法添加该注解,以声明该方法用于覆盖父类中的方法,如果该方法不是覆盖父类中的方法,将会在编译时报错。
1.2、代码示例
/*
关于JDK lang包下的Override注解
源代码:
public @interface Override {
}
标识性注解,给编译器做参考的。
编译器看到方法上有这个注解的时候,编译器会自动检查该方法是否重写了父类的方法。
如果没有重写,报错。
这个注解只是在编译阶段起作用,和运行期无关!
*/
// @Override这个注解只能注解方法。
// @Override这个注解是给编译器参考的,和运行阶段没有关系。
// 凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错。
//@Override
public class AnnotationTest02 {
//@Override
private int no;
@Override
public String toString() {
return "toString";
}
}
2、@Deprecated
2.1、对不应该在使用的方法添加注释, 当编程人员使用这些方法时, 将会在编译时显示提示信息,它与 javadoc 里的 @deprecated 标记有相同的功能。
2.2、代码示例
// 表示这个类已过时。
@Deprecated
public class AnnotationTest03 {
@Deprecated
private String s;
public static void main(String[] args) {
AnnotationTest03 at = new AnnotationTest03();
at.doSome();
}
@Deprecated
public void doSome(){
System.out.println("do something!");
}
// Deprecated这个注解标注的元素已过时。
// 这个注解主要是向其它程序员传达一个信息,告知已过时,有更好的解决方案存在。
@Deprecated
public static void doOther(){
System.out.println("do other...");
}
}
class T {
public static void main(String[] args) {
AnnotationTest03 at = new AnnotationTest03();
at.doSome();
AnnotationTest03.doOther();
try {
Class c = Class.forName("java.util.Date");
Object obj = c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、@SuppressWarnings
3.1、与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数值都是已经定义好了的,我们选择性的使用就好了
3.2、参数说明:
参数 | 说明 |
---|---|
deprecation | 使用了过时的类或方法时的警告 |
unchecked | 执行了未检查的转换时的警告,比如当使用集合时没有使用泛型来指定集合保存的类型 |
fallthrough | 当switch程序直接通往下一种情况而没有break时的警告 |
path | 在类路径、源文件路径中有不存在的路径时的警告 |
serial | 当在可序列化的类上缺少 serialVersionUID 定义时的警告 |
finally | 任何 finally 子句不能正常完成时的警告 |
all | 关于以上所有情况的警告 |
rawtypes | 忽略传参时没有指定泛型的警告 |
三、自定义注解
1、使用@interface自定义注解
1.1、自定义注解代码示例
package annotation;
//自定义注解
public @interface MyAnnotation {
//???
}
package annotation;
// 注解修饰注解。
@MyAnnotation
public @interface OtherAnnotation {
//???
}
2、自定义注解的使用
2.1、默认情况下。注解可以出现在任意位置
package annotation;
//默认情况下注解可以出现在任意位置
@MyAnnotation
public class AnnotationTest1 {
@MyAnnotation
private int num;
@MyAnnotation
public AnnotationTest1() {
}
public static void test() {
@MyAnnotation
int a = 10;
}
public static void test2(@MyAnnotation
String naem,
@MyAnnotation
int age) {
}
}
@MyAnnotation
interface MyInterface {
}
@MyAnnotation
enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
3、自定义注解添加变量
3.1、自定义注解也可以添加变量,变量类型只能是基本数据类型、String类型、Class类型、枚举类型、数组类型,并且参数不能为空。
package annotation2;
public @interface MyAnnotation {
//添加属性
String name();
}
package annotation2;
/*
@MyAnnotation:只添加这个,程序报错
如果一个注解中有属性,那么注解时必须给
这个属性赋值,除非该属性指定了默认值
@MyAnnotation(属性名=属性值,属性名=属性值,属性名=属性值)
*/
public class AnnotationTest1 {
@MyAnnotation(name = "jack")
public void doSome() {
}
}
3.2、如果一个注解中只有一个属性,并且该属性名字为value,则使用该注解的时候,属性名可以省略
package annotation2;
public @interface MyAnnotation {
//添加一个value属性
String value();
}
package annotation2;
public class AnnotationTest1 {
@MyAnnotation(value = "jack")
public void doSome() {
}
//如果一个注解中只有一个属性,并且名字为value,使用时可以省略属性名
@MyAnnotation("jack")
public void doOther() {
}
}
3.3、为注解赋默认值
package annotation2;
public @interface MyAnnotation {
//添加属性,并赋予默认值
String name();
int age() default 20;
}
package annotation2;
public class AnnotationTest1 {
//注解有默认值值时,使用注解时可以重新赋值,也可以不赋值,使用默认值
@MyAnnotation(name = "jack")
public void doSome() {
}
@MyAnnotation(name = "mark", age = 18)
public void doOther() {
}
}
四、注解的属性类型
1、注解中的属性类型
1.1、注解中的属性类型可以是byte、short、int、long、float、double、boolean、char、String、Class、枚举类型,及以上每一种类型的数组类型
public @interface MyAnnotation {
/*
注解当中的属性可以是哪一种类型?
属性的类型可以是:
byte short int long float double boolean char String Class 枚 举类型以及以上每一种的数组形式。
*/
int value1();
String value2();
int[] value3();
String[] value4();
Season value5();
Season[] value6();
Class parameterType();
Class[] parameterTypes();
}
1.2、测试案例
//定义枚举Season
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
public @interface OtherAnnotation {
//年龄属性
int age();
//邮箱属性
String[] email();
//季节属性,Season是枚举类型
Season[] arr();
}
public class OtherAnnotationTest {
//数组使用大括号括起来
@OtherAnnotation(age=18,email={"zs@qq.com","lisi@qq.com"},arr={Season.SPRING,Season.AUTUMN})
public void doSome() {
}
//当数组中只有一个元素时,大括号可以省略
@OtherAnnotation(age=20,email="zs@qq.com",arr=Season.WINTER)
public void doOther() {
}
}
五、元注解
1、@Target注解
1.1、当我们的自定义注释不断的增多也比较复杂时,就会导致有些开发人员使用错误。为此,Java 提供了一个ElementType枚举类型来控制每个注释的使用范围,比如说某些注释只能用于普通方法,而不能用于构造函数等
//源代码
package java.lang.annotation;
public enum ElementType {
TYPE,/** Class, interface (including annotation type), or enum
declaration */
FIELD,/** Field declaration (includes enum constants) */
METHOD,/** Method declaration */
PARAMETER,/** Parameter declaration */
CONSTRUCTOR,/** Constructor declaration */
LOCAL_VARIABLE,/** Local variable declaration */
ANNOTATION_TYPE,/** Annotation type declaration */
PACKAGE /** Package declaration */
}
参数 | 使用范围 |
---|---|
TYPE | 适用于class、interface、enum |
FIELD | 适用于field |
METHOD | 适用于method |
PARAMETER | 适用于parameter |
CONSTRUCTOR | 适用于constructor |
LOCAL_VARIABLE | 适用于局部变量 |
ANNOTATION_TYPE | 适用于annotation类型 |
PACKAGE | 适用于package |
1.2、测试案例
package annotation3;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//只允许该注解可以标注类和方法
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
//value属性
String value() default "好好学习";
}
package annotation3;
@MyAnnotation("天天向上")
public class MyAnnotationTest {
/*
属性和构造方法不能使用该注解,报错
@MyAnnotation
int i = 0;
@MyAnnotation
public MyAnnotation() {
}
*/
@MyAnnotation
public void doSome() {
}
}
package annotation3;
public class ReflectAnnotationTest {
public static void main(String[] args) throws ClassNotFoundException {
//获取这个类
Class c = Class.forName("annotation3.MyAnnotationTest");
//判断这个类上面有没有@MyAnnotation
System.out.println(c.isAnnotationPresent(MyAnnotation.class)); //true
if (c.isAnnotationPresent(MyAnnotation.class)) {
//获取注解对象
MyAnnotation myAnnotation = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
//类上面的注解对象:@annotation3.MyAnnotation(value="天天向上")
System.out.println("类上面的注解对象:" + myAnnotation);
//获取注解对象的属性,和调用接口一样
String value = myAnnotation.value();
System.out.println("注解对象的属性:" + value); //注解对象的属性:天天向上
}
//判断String类上面是否存在这个注解
Class str = Class.forName("java.lang.String");
System.out.println(str.isAnnotationPresent(MyAnnotation.class));
}
}
2、@Retention注解
2.1、源码说明
//原码
package java.lang.annotation;
public enum RetentionPolicy {
/*
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler
and
* retained by the VM at run time, so they may be read reflectively.
*/
RUNTIME
}
2.2、参数说明
参数 | 说明 |
---|---|
RetentionPolicy.SOURCE | 编译器使用后,直接丢弃这种策略的注释 |
RetentionPolicy.CLASS | 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解。 这是默认值 |
RetentionPolicy.RUNTIME | 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以通过反射获取该注解 |
3、@Documented注解
3.1、Java 提供的Documented 元注释跟 Javadoc 的作用是差不多的,其实它存在的好处是开发人员可以定制Javadoc 不支持的文档属性,并在开发中应用。如果你要使用@Documented 元注释,你就得为该注释设置 RetentionPolicy.RUNTIME 保持性策略。
4、@Inherited注解
4.1、控制注释是否会影响到子类