目录
- 1.注解概述
- 2.Java 提供的 5 个基本注解
- 3.注解入门使用
- 4.自定义注解
- 5.通过反射获取注解信息
本文章部分笔记整理来自https://www.bilibili.com/video/BV12k4y1q73J?p=333
1.注解概述(1)注解 (Annotation),也称为元数据,它是一种代码级别的说明,是 JDK 1.5 及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、变量、参数等的前面,用来对它们进行说明。其作用大致可以分为以下三类:
编写文档通过代码里标识的注解生成文档【例如生成文档doc文档】代码分析通过代码里标识的注解对代码进行分析(例如在框架中通常会采用注解的方式来代替配置文件,再通过反射获取注解中配置的信息)编译检查通过代码里标识的注解让编译器能够实现基本的编译检查【例如Override】(2)注解本质是一个继承了Annotation 的特殊接口:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
public interface Override extends Annotation{
}
(3)注解只有被解析之后才会生效,常见的解析方法有以下两种:
- 编译期直接扫描:编译器在编译 Java 代码的时候扫描对应的注解并处理,比如某个方法使用 @Override 注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。
- 运行期通过反射处理:像框架中自带的注解,比如 Spring 框架的 @Value 、@Component 都是通过反射来进行处理的。
(4)Java 注解主要解决了以下几个问题:
- 代码可读性和维护性问题:通过给代码添加注解,可以为代码提供更加清晰明确的含义和解释,从而提高代码可读性和代码维护性。同时,通过注解,还可以为代码提供更加详细的说明和附加信息。
- 配置和约束问题:注解可以为代码提供更加灵活和细致的配置和约束信息,从而可以在编译期或运行期自动化地进行检查和约束,防止代码出现潜在的问题。比如,可以使用注解来标记某个方法或变量是过时的,或者限制某个方法的访问权限等。
- 元数据管理问题:注解可以提供元数据信息,从而可以通过反射来获取代码的元数据,进而可以对元数据进行自动化管理和处理。比如,可以使用注解来自动生成文档或 API,或者从源代码中提取相关的元数据信息。
总的来说,Java 注解能够为程序员提供更加便捷和灵活的编程方式,同时也有助于提高代码的可读性和维护性。它主要解决了在代码开发、配置管理、约束检查、元数据处理等方面面临的一些问题。
2.Java 提供的 5 个基本注解 注解作用@Override当子类重写父类方法时,子类可以加上这个注解,那么可以确保子类确实重写了父类的方法,避免出现低级错误@Deprecated用于表示某个程序元素类,方法等已过时,当其他程序使用已过时的类,方法时编译器会给出警告(即删除线)@SuppressWarnings被该注解修饰的元素以及该元素的所有子元素取消显示编译器警告,例如修饰一个类,那它的字段,方法都是显示警告@SafeVarargs断定声明的构造函数和方法的主体不会对其可变参数执行潜在的不安全的操作@Functionallnterface保证修饰的接口只有一个抽象方法(注意该注解只能修饰接口)其中注解@SuppressWarnings(“参数”)后面的括号中不同的参数可以表示去除不同的警告类型:
参数去除的警告类型unused变量未使用警告deprecation使用了不赞成使用的类或方法时的警告unchecked执行了未检查的转换时的警告,例如当使用集合时没有用泛型(Generics) 来指定集合保存的类型fallthrough当 Switch 程序块直接通往下一种情况而没有 Break 时的警告path在类路径、源文件路径等中有不存在的路径时的警告serial当在可序列化的类上缺少 serialVersionUID 定义时的警告finally任何 finally 子句不能正常完成时的警告all关于以上所有情况的警告 3.注解入门使用package jf_Annotation;
//案例: 注解入门
//忽略所有警告信息
@SuppressWarnings("all")
public class AnnotationDemo01 {
//标记方法已过时,不推荐使用(但是还是可以使用)
@Deprecated
public static void show(){
System.out.println("show!");
}
public static void main(String[] args) {
//需求1: 调用show()方法
//在调用时,IDEA提示的show()方法上会有删除线,表示该方法已经过时,不推荐使用
show();
//需求2: 去重警告
int a = 10;
}
}
4.自定义注解
(1)自定义注解 MyAnnotation
package jf_Annotation;
/*
自定义注解里边可以写的内容包括:
1. 八种基本类型.
2. String类型, Class类型, 枚举类型, 注解类型.
3. 以上所有类型的数组形式.
*/
//自定义的注解类型, 注解名为MyAnnotation
public @interface MyAnnotation {
//该注解有三个属性: name, age, arr
public String name() default "张三";
public int age();
public String[] arr();
//public MyAnnotation2 ma();
}
@interface MyAnnotation2{}
(2)使用自定义注解
package jf_Annotation;
/*
定义注解Book1,包含属性如下:
String value() 书名
double price() 价格,默认值为 100
String[] characters() 人物
*/
public @interface Book1 {
public String value(); //书名
public double price() default 100; //价格
public String[] characters(); //人物
}
package jf_Annotation;
/*
定义注解Book2
知识点:当注解中只有一个属性且名称是value,在使用注解时给value属性赋值可以直接给属性值,无论value是单值元素还是数组类型。
*/
//自定义的注解Book2
public @interface Book2 {
public String value();
}
package jf_Annotation;
//案例: 自定义类BookShelf, 表示书架, 类上有注解
/*
注意事项:
1.如果属性有默认值,则使用注解的时候,这个属性可以不用赋值。
2.如果属性没有默认值,那么在使用注解时一定要给属性赋值。
*/
@Book1(value = "西游记", characters= {"孙悟空", "猪八戒"})
@Book2(value = "三国演义") //或者@Book2("三国演义")
public class BookShelf {
public void showBook() {
}
}
5.通过反射获取注解信息
(1)自定义注解
package jf_Annotation.exercise;
/*
元注解:元注解就是用来对自定义的注解进行一些限定的, 常用的元注解有:
@Target 用来限定注解可以写在哪个地方, 如果不写默认是人可以地方都可以用此注解.
@Retention 用来定义注解的生命周期(即: 注解的有效范围)
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE}) //限定此注解只能写到方法上或者类上
@Retention(RetentionPolicy.RUNTIME) //说明该注解在: 源码阶段, 字节码阶段, 程序运行阶段都有效
public @interface Book {
public String value(); //书名
public double price() default 50; //价格
public String[] characters(); //人物
}
(2)使用自定义注解
package jf_Annotation.exercise;
//案例: 自定义的类, 表示书架, 类上有注解.
@Book(value = "西游记",price = 60,characters = {"孙悟空", "猪八戒"})
public class BookShelf {
@Book(value = "三国演义", characters = {"刘备", "关羽", "张飞"})
public void showBook() {
}
}
(3)获取注解信息 通过Java技术获取注解数据的过程则称为注解解析,Anontation是所有注解类型的公共接口,AnnotatedElement接口定义了与注解解析相关的方法,常用的方法以下四个:
方法作用boolean isAnnotationPresent(Class annotationClass);判断当前对象是否有指定的注解,有则返回true,否则返回false。T getAnnotation(Class annotationClass);获得当前对象上指定的注解对象。Annotation[] getAnnotations();获得当前对象及其从父类上继承的所有的注解对象。Annotation[] getDeclaredAnnotations();获得当前对象上所有的注解对象,不包括父类的。package jf_Annotation.exercise;
//注解常用的套路: 用注解的方式来替代配置文件, 然后通过反射的方式获取注解中的配置信息
import java.lang.reflect.Method;
import java.util.Arrays;
public class BookTest {
public static void main(String[] args) throws NoSuchMethodException {
//需求1: 获取BookShelf类上的注解
method01();
System.out.println("-------------------------------");
//需求2: 获取showBook方法上的注解
method02();
}
public static void method01() {
//1. 获取BookeShelf类的字节码文件对象
Class clazz = BookShelf.class;
//2. 判断BookShelf类是否有Book注解
if(clazz.isAnnotationPresent(Book.class)) {
//3. 如果有, 就获取BookShelf类上的Book注解
Book book = clazz.getAnnotation(Book.class);
//4. 输出注解的值,此时将注解当作对象使用即可
System.out.println("书名: " + book.value());
System.out.println("价格: " + book.price());
System.out.println("人物: " + Arrays.toString(book.characters()));
}
}
public static void method02() throws NoSuchMethodException {
//1. 获取BookeShelf类的字节码文件对象
Class clazz = BookShelf.class;
//2. 根据字节码文件对象, 获取指定的方法对象
Method method = clazz.getMethod("showBook");
//3. 判断showBook方法是否有Book注解
if(method.isAnnotationPresent(Book.class)) {
//4. 如果有, 就获取showBook方法上的Book注解
Book book = method.getAnnotation(Book.class);
//5. 输出注解的值.
System.out.println("书名: " + book.value());
System.out.println("价格: " + book.price());
System.out.println("人物: " + Arrays.toString(book.characters()));
}
}
}
结果如下: