注解(Annotation) 也叫元数据 是一种代码级别的说明
它是JDK1.6及以后版本引入的一个特性 与类、接口、枚举是在同一个层次
它可以声明在包、类、字段、方法、局部变量、方法参数等的前面
用来对这些元素进行说明、注释
作用:本来可能需要很多配置文件 需要很多逻辑才能实现的内容 就可以使用一个或者多个注解来替代
这样就使得编程更加简洁 代码更加清晰
原理:本质上就是一个继承自java.lang.annotation.Annotation
的接口
- 注解的写法
@XXX(一些信息)
2. 注解放置在哪里
类的上面 属性上面 方法上面 构造方法上面 参数前面
3. 注解的作用
3.1 用来充当注释的作用(仅仅是一个文件的说明) 比如 @Deprecated 表示过期了
3.2 用来做代码的检测(验证) 比如 继承类 或 实现接口时 需重写方法 他会在方法上面写有@Override
如果你的方法不是重写的方法 它会报错
3.3 可以携带一些信息(内容) 文件和注解是经常会用到的
(但是文件和代码不在一个地方 注解和代码在一个地方)
4. Java中有一些人家写好的注解
4.1 @Deprecated 用来说明方法是废弃的
4.2 @Override 用来做代码的检测 检测此方法是否是一个重写
4.3 @SuppressWarnings() 括号里的信息是String[] {“ “,” “} 如果只写一个信息可以不写大括号
@SuppressWarnings(“unused”) 变量定义后未被使用 不想让系统报错
@SuppressWarnings(“serial”) 类实现了序列化接口 不添加序列化ID号 不想让系统报错
@SuppressWarnings(“rawtypes”) 集合没有定义泛型 不想让系统报错
@SuppressWarnings(“deprecation”) 方法废弃后会有横线 这个可以让横线没有
@SuppressWarnings(“unchecked”) 出现了泛型问题 可以不检测
@SuppressWarnings(“all”) 包含了以上所有 都可以不检查(最好不使用)
5. 注解中可以有成员 也可以不携带
注解中的成员类型不能随意写 只能是如下类型
5.1 基本数据类型
5.2 String类型
5.3 枚举类型enum
5.4 注解类型
5.5 数组类型[] 数组里的内容需要是上述四种类型
如果注解只有一个成员 则成员名必须取名为value() 在使用时可以忽略成员名和赋值号 =
6. 如何自己描述一个注解类型
6.1 通过@interface来定义一个新的注解类型
6.2 发现写法与注解非常相似(可以用接口的特点来记忆)
可以描述 public static final 的属性 比较少见
可以描述 public abstract 的方法 方法要求必须有返回值 返回值类型是上述五种
6.3 自己定义的注解若想拿来使用 光定义还不够 还需要很多细致的说明(需利用Java提供好的注解来说明)
元注解(也是注解 不是拿来使用的 是用来说明注解的)
6.3.1 @Target() 即注解的作用域 描述当前注解可以放置在哪里写的
括号里是一个数组 数组类型是ElementType类型(枚举)
取值:
ElementType.TYPE 放在类、接口上
ElementType.CONSTRUCTOR 放在构造方法上
ElementType.FIELD 放在属性(字段)上
ElementType.METHOD 放在方法上
ElementType.PACKAGE 放在包上
ElementType.PARAMETER 参数声明
ElementType.LOCAL VARIABLE 局部变量声明
若不想写 ElementType. 可以在导包的时候麻烦一点 静态导入
6.3.2 @Retention 描述当前注解的生命周期
注解可以存在于三个位置 源代码文件 字节码文件 内存中执行
源代码文件 ——-> 编译 ——-> 字节码文件 ——-> 加载 ——-> 内存中执行
SOURCE CLASS RUNTIME
@Retention(RetentionPolicy.RUNTIME) 最大的范围 就算在内存中也能找到注解(通过反射获取)
@Retention(RetentionPolicy.CLASS) 编译时记录到class中 运行时丢弃
@Retention(RetentionPolicy.SOURCE) 只在源码显示 编译时丢弃
6.3.3 @Inherited 一个标识性的元注解 描述当前注解是否能被子类对象继承
6.3.3 @Document 描述当前注解是否能被文档记录 即生成javadoc时会包含注解
6.4 使用自己的注解
6.4.1 在注解里面描述了一个方法 方法没有参数 但方法有返回值String 即String[] test();
在使用注解时 注解让我们传参数
这可以理解为 注解里的方法 将我们传递给它的参数 搬运走了 交给别人
6.4.2 使用别人写好的注解不用写方法名 我们自己定义的注解必须写方法名才能传递参数
如果我们定义的注解只有一个方法 方法名字叫value 在使用时就可以省略方法名 即String value();
原本应为@MyAnnotation(test=”abc”) 现在为可以@MyAnnotation(“abc”)
如果传递的参数是一个数组 数组内只有一个元素 可以省略{ }
如果方法有两个以上 每个方法必须写名字 顺序不分先后
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String[] value();
//String[] value() default "abc";
//此时注解可以不传参 默认传的参数是abc
//不写default必须传参
}
public class Test {
@MyAnnotation("abc")//注意注解后面不要加分号
public String name;
@MyAnnotation("abc")
public void eat() {
}
}
如何解析注解内携带的信息(反射机制)
1 获取类Class 比如 类名.class
2 获取类中成员 类 属性 方法 构造方法
3 获取注解对象 Annotation a = 成员.getAnnotation(注解类型.class);
4 获取注解对象对应的类 Class aclass = a.getClass();
5 获取注解类中的value()方法 Method am = aclass.getMethod(“value”);
6 执行value()方法即可得到注解中的信息 am.invoke(a);
注意value()方法是属于注解对象的public class Person {
@MyAnnotation({"小兰","女","18"})
private String name;
@MyAnnotation({"小黑","男","17"})
public void eat(){
}
}
获取属性上的注解:
try {
//解析Person类中属性上面的注解信息 需要用到反射技术
//1.获取Person对应的Class
Class clazz = Person.class;
//2.获取Person里的属性
Field field = clazz.getDeclaredField("name");
//3.通过field获取注解对象
//方式一:正常的对象调用执行过程
//Annotation a = field.getAnnotation(MyAnnotation.class);
// 我们的注解之所以是注解 是通过元注解来描述的
// 前面大的类型来接收 后面是一个小的类型 这里相当于是一个多态的效果
// 故父类的引用指向子类的对象 是调不到里面独有的方法的
//所以应该用
// MyAnnotation ma = (MyAnnotation) field.getAnnotation(MyAnnotation.class);
// //4.利用ma对象执行注解中的value方法 就帮我们把信息搬过来了 可以得到注解里的信息
// String[] values = ma.value();
// System.out.println(values[0]);
//但是不是所有的方法都叫value 所以这个方式不太好
//方式二:通过反射
Annotation a = field.getAnnotation(MyAnnotation.class);
//4.利用反射执行a中的value方法
Class aClazz = a.getClass();//利用a对象获取它对应的class 即注解对应的类
//5.通过aclazz获取里面的value方法
Method aMethod = aClazz.getMethod("value");
//6.执行value方法 获取传递的信息
String[] values = (String[]) aMethod.invoke(a);
System.out.println(values[2]);
获取方法上的注解:
try {
//解析Person类中属性上面的注解信息 需要用到反射技术
//1.获取Person对应的Class
Class clazz = Person.class;
//2.获取Person里的方法
Method m = clazz.getMethod("eat");
//3.获取方法上的注解对象
Annotation a = m.getAnnotation(MyAnnotation.class);
//4.获取注解对应的类
Class aClass = a.getClass();
//5.获取注解里的value方法
Method myMethod = aClass.getMethod("value");
//6.执行value方法 注意方法所属对象为a
String[] values = (String[]) myMethod.invoke(a);
System.out.println(values[0]);
} catch (Exception e) {
e.printStackTrace();
}