学习目的
了解java注解的概念
掌握常用注解的使用
了解自定义注解
一.注解
概念
注解,又叫"注释类型",全称Annotation。注解是一种引用数据类型,编译之后也是生成xxx.class文件。
使用地点
用于类上:
用于属性上:
用于方法上:
用于局部变量上:
用于接口上:
用于枚举类上:
用于注解上:注解用于"新注解"的定义上,该注解则是"元注解"。
语法格式
// 注解的使用格式
@注解类型名
@MyAnnotation //注解用于 类
public class AnnotationTest01 {
@MyAnnotation //注解用于 类属性
private int no;
@MyAnnotation //注解用于 构造方法
public AnnotationTest01(){}
@MyAnnotation //注解用于 方法
public static void m1(){
@MyAnnotation
int i = 100; //注解用于 局部变量
}
@MyAnnotation //注解用于 局部变量
public void m2(@MyAnnotation String name,@MyAnnotation int k){
}
}
@MyAnnotation //注解用于 接口
interface MyInterface {
}
@MyAnnotation //注解用于 枚举类
enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
1.1 常用注解
概念
在java中,JDK内置的java.lang包下提供了许多常用的注解(注释类型)。
常用注解类型
Deprecated:用@Deprecated注释的程序元素,表示该元素已过时、不鼓励继续使用;
Override:表示一个方法声明打算重写超类中的另一个方法声明。;
SuppressWarnings:指示应该在注释元素中取消显示 指定的编译器警告(包含在该注释元素中的所有程序元素)。
1.1.1 Deprecated(掌握)
概念
使用Deprecated注解标注的元素,表示不支持/不鼓励 程序员使用该元素,通常是因为该元素存在危险 或拥有其他更好的选择。
特点
标注元素:指示该元素不鼓励/不赞成被使用;
标注方法:指示该方法不鼓励/不赞成被重写;
标注代码块:指示在该代码块中不鼓励/不赞成进行重写;
使用出现警告:若使用了Deprecated标注的元素,编译和运行都可以通过,但是会出现三角提示符号--Warning;
已过时但可以被查看:Deprecated底层使用了元注解Retention标注,允许在运行时捕获查看。
作用
被Deprecated标注后,在使用不被赞成的程序元素 或在不被赞成的代码中执行重写时,编译器会发出警告。一般用于版本更新时注解旧版本的元素已过时。
1.1.2 Override(掌握)
概念
Override注解用于表示一个方法打算重写父类中的另一个方法。如果一个方法利用Override进行注解但没有重写父类方法(重写的方法名不同或参数不同),编译器会生成错误息。
特点
被Override注解标注的方法,一定是重写父类的方法;
Override只能编写在方法上,不能用于标注类、属性和变量;
仅限于源文件查看,不被编译:Override底层使用了元注解Retention标注,仅限于java源文件中可以查看。
作用
提供给编译器参考,用于提前检查方法重写错误。
1.1.3 SuppressWarnings
概念
SuppressWarnings用于指示应该在注释元素中取消显示指定的编译器警告,一般是用于取消Deprecated带来的警告。
特点
标注元素:SuppressWarnings标注的元素会取消编译警告;
标注方法:SuppressWarnings标注的方法会取消警告;
标注类:SuppressWarnings标注的类会取消警告;
警告超集:在给定元素中取消显示的警告集是所有包含元素中取消显示的警告的超集。例如注释一个类来取消显示某个警告,同时注释同类中的一个方法来取消显示另一个警告,那么将在此方法中同时取消显示这两个警告。
作用
被SuppressWarnings注释的元素,会被取消显示警告。
1.2 元注解
概念
用来标注“注解类型”的“注解”,称为元注解。
常见元注解
Target:元注解,用来标注注解可以(出现)使用在哪个位置上;
Retention:元注解,用来标注注解最终保留的位置(持久化到哪里)。
1.2.1 Target
概念
Target指示注释类型所适用的程序元素的种类,是用来标注"注解类型"的"元注解"。被Target标注后,"被标注的注解"可以指定出现在哪些位置上。
Target标注特点
@Target(ElementType.METHOD):表示"被标注的注解"只能出现在方法上;
@Target(value={CONSTRUCTOR}):表示"被标注的注解"只能出现在构造器上;
@Target(value={FIELD}):表示"被标注的注解"只能出现在属性上;
@Target(value={ LOCAL_VARIABLE}):表示"被标注的注解"只能出现在局部变量上;
@Target(value={METHOD}):表示"被标注的注解"只能出现在方法上;
@Target(value={ PACKAGE}):表示"被标注的注解"只能出现在包上;
@Target(value={ MODULE}):表示"被标注的注解"只能出现在模块上;
@Target(value={PARAMETER}):表示"被标注的注解"只能出现在参数上;
@Target(value={ TYPE}):表示"被标注的注解"只能出现在类上。
Target底层
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
// Target注解的属性:ElementType[]枚举数组类型
ElementType[] value();
}
// ElementType枚举类:作为Target元注解的属性
public enum ElementType {
TYPE, //类
FIELD, //属性
METHOD, //普通方法(区分于构造方法)
PARAMETER, //参数
CONSTRUCTOR, //构造器
LOCAL_VARIABLE, //本地局部变量
ANNOTATION_TYPE, //注解
PACKAGE, //包
TYPE_PARAMETER, //
TYPE_USE //
}
1.2.2 Retention
概念
Retention指示注释类型的注释要保留多久(保留的位置:源文件、编译时、运行时),是用来标注"注解类型"的"元注解"。被Retention标注后,表明"被标注的注解"的注解最终保存在哪里。
Retention标注
@Retention(RetentionPolicy.SOURCE):表示该注解只被保留在java源文件中(该注解不会被编译到class文件中);
@Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中;
@Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以被反射机制所读取。
Retention与RetentionPolicy
//元注解
public @interface Retention {
//Retention元注解的属性为 RetentionPolicy枚举类型
RetentionPolicy value();
}
//RetentionPolicy枚举类:作为Retention元注解的属性类型
public enum RetentionPolicy {
SOURCE, //保留于源文件
CLASS, //保留于字节码文件
RUNTIME //保留于运行时并可被反射获取
}
//同下,只是省略value=:@Retention(value=RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation{
//自定义注解
}
1.3 自定义注解
概念
自定义注解组成
@interface标识符:注解的定义必须带有@interface标识符,其作用类似于Class和interface一样;
注解属性:每一个自定义注解可以拥有/也可以不拥有属于自己本身的属性,注解属性的格式是属性类型 属性名();
属性值:不管是自定义注解还是JDK提供的注解,使用时若原注解拥有属性,则必须给注解的属性指定值(除非有default默认值);
属性类型:属性类型和类的变量类型一样,不同的属性确定注解的属性值类型,注解属性的类型可以为基本数据类型、String、引用类型、枚举类型、数组类型以及以上类型所有数组类型;
value()属性:当且仅当一个注解中只有一个属性,且属性名称为value()时,使用该注解时可以不用写出属性 = ?,直接编写属性的值即可。
格式
// 注解的定义格式
[修饰符列表] @interface 注解类型名{
属性类型 属性名();//定义注解的属性
}
//自定义"注解":与接口的定义类似
//@Target(ElementType.METHOD) //元注解,标注该自定义注解指定使用在"方法上"
//@Retention(RetentionPolicy.SOURCE) //元注解,标注该自定义注解仅保存在.java源文件中
public @interface MyAnnotation {
//在注解中定义属性, 看着像方法,但实际上是注解的属性
//name属性
String name();
//颜色属性
String color();
//年龄属性
int age(); //属性没有初始化值,使用该注解时必须给属性赋值
int age() default 25; //属性指定默认值,使用该注解时可以不用初始化值
}
使用自定义注解(参考JDK内置的注解)
public class MyAnnotationTest {
// 以下使用注解 出现报错
/*@MyAnnotation //未给注解的属性赋值(default属性除外)
public void doSome(){
}*/
//@MyAnnotation(属性名=属性值,属性名=属性值,属性名=属性值)
//指定name属性的值
@MyAnnotation(name = "zhangsan")
public void doSome(){
}
//指定name属性和color属性的值
@MyAnnotation(name = "zhangsan", color = "红色")
public void doSome(){
}
//指定name属性、color属性和age属性的值(age属性有default默认值,也可以不指定)
@MyAnnotation(name = "zhangsan", color = "红色",age="23")
public void doSome(){
}
}
1.4 反射与注解
概念
可通过反射机制获取字节码文件的注解,即"字节码的注释信息"。
获取机制
被获取的注解必须是声明了可被反射获取的,即一定是被@Retention(RetentionPolicy.RUNTIME)进行注解标注。
示例
// 自定义注解 MyAnnotation
@Target({ElementType.TYPE, ElementType.METHOD}) //只允许该注解可以标注类、方法
//@Retention(RetentionPolicy.Class) // 此注解不可以被反射,仅在源文件中
@Retention(RetentionPolicy.RUNTIME) // 此注解可以被反射
public @interface MyAnnotation {
// 自定义注解属性
String value() default "北京大兴区";
}
// 自定义注解使用
@MyAnnotation("上海浦东区")
public class MyAnnotationTest {
//@MyAnnotation //@MyAnnotation不允许标注属性
int i;
//@MyAnnotation //@MyAnnotation不允许标注构造方法
public MyAnnotationTest(){
}
@MyAnnotation
public void doSome(){
//@MyAnnotation //@MyAnnotation不允许标注局部变量
int i;
}
}
//反射获取注解
public static void main(String[] args) throws Exception{
// 获取使用了MyAnnotation注解的类
Class c = Class.forName("com.java.annotation.MyAnnotationTest");
// 判断该类上面是否拥有@MyAnnotation
if(c.isAnnotationPresent(MyAnnotation.class)){
// 获取该注解对象
MyAnnotation myAnnotation = (MyAnnotation)c.getAnnotation(MyAnnotation.class);
// 获取注解对象的属性
String value = myAnnotation.value();
System.out.println(value);
}
// 判断String类上面是否存在MyAnnotation注解
Class stringClass = Class.forName("java.lang.String");
stringClass.isAnnotationPresent(MyAnnotation.class); // false
}
1.5 Spring注解使用
概念
常见面试题
注释和注解的区别?
答: