注解
一、概述、作用
1、概述
- Java注解(Annotation)又称Java标注,是JDK5.0引入的一种注释机制。
- Java语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
2、作用
-
对Java中类、方法、成员变量做标记,然后进行特殊的处理,至于到底做何种处理由业务需求来决定。
-
例如:
-
JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。
-
-
举例说明:
-
大家应该都看过隋唐英雄吧!
-
李世民和好多义士要反朝廷,但是皇上又派李元霸去打这些义士,那么李世民说这些都是好人,不能打;
-
但是由于李元霸神经有问题,不能辨别哪些是好人,哪些是坏人!
-
李世民就说:“你们都带上红领巾,这样李元霸就不会打你们了!!如果你们不戴,那就会被李元霸打“。
-
总结
- 注解的作用是?
- 对Java中类、方法、成员变量做标记,然后进行特殊对待、处理。
- 例如JUnit框架中,标记了@Test的方法可以被当成测试方法执行,没有标记的就不行。
二、 自定义注解
- 自定义注解就是自己做一个注解来使用。
1、格式
public @interface 注解名称 {
public 属性类型(Java支持的数据类型都可以写) 属性名() default 默认值;
}
package com.app.d8_annotation;
/**
* 自定义注解
*/
public @interface MyBook {
// 默认是公共的,public可以省略不写
// public String bookName();
String bookName(); // 书名
String[] authors(); // 作者
double price(); // 价格
}
package com.app.d8_annotation;
/**
目标:学会自定义注解,掌握其定义格式和语法
*/
// 注解可以标记类
@MyBook(bookName = "《JavaSE基础》", authors = {"张飞", "关羽"}, price = 9.9)
public class AnnotationDemo {
// 注解可以标记成员变量
@MyBook(bookName = "《JavaSE基础》", authors = {"张飞", "关羽"}, price = 9.9)
private String name;
// 注解可以标记构造器
@MyBook(bookName = "《JavaSE基础》", authors = {"张飞", "关羽"}, price = 9.9)
private AnnotationDemo() {
}
// 注解可以标记成员方法
public void run() {
System.out.println("跑起来");
}
// 注解可以标记常量
@MyBook(bookName = "《JavaSE基础》", authors = {"张飞", "关羽"}, price = 9.9)
public static final String COUNTRY = "中国";
// 注解可以标记main方法
@MyBook(bookName = "《JavaSE基础》", authors = {"张飞", "关羽"}, price = 9.9)
public static void main(String[] args) {
// 注解可以标记局部变量
@MyBook(bookName = "《JavaSE基础》", authors = {"张飞", "关羽"}, price = 9.9)
int a = 10;
}
}
2、特殊属性
-
value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写!!
-
但是如果有多个属性,且多个属性没有默认值,那么value名称不能省略的!!
总结
-
如何自定义注解?
public @interface 注解名称 { public 属性类型 属性名() default 默认值; }
-
特殊属性value的特点是?
- 属性中如果只有value一个,则value名称可以省略不写!!
- 属性中如果有多个属性,且多个属性都没有设置默认值,则value名称不能省略不写!!
- 属性中如果有多个属性,且多个属性都有设置默认值,则value名称可以省略不写!!
三、元注解
- 就是放在注解上面的注解。
1、常见元注解
(1)@Target
-
约束自定义注解只能在哪些地方使用
-
@Target
中可使用的值定义在ElementType
枚举类中,常用值如下:值名称 作用 TYPE 此注解只能作用于类、接口 FIELD 此注解只能作用于成员变量 METHOD 此注解只能作用于成员方法 PARAMETER 此注解只能作用于方法参数 CONSTRUCTOR 此注解只能作用于构造器 LOCAL_VARIABLE 此注解只能作用于局部变量
范例
package com.app.d8_annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
// MyTest注解只能作用于成员变量、成员方法上,其他不行!
@Target({ElementType.FIELD, ElementType.METHOD}) // 元注解: @Target(约束自定义注解只能作用在某些地方)
/**
* 自定义注解
*/
public @interface MyTest {
}
package com.app.d8_annotation;
/**
* 目标:认识元注解
* 概述:它是放在注解上面的注解
*/
// @MyTest // 报错:@Target注解起了作用(MyTest注解只能作用于成员变量和成员方法上)
public class AnnotationDemo2 {
@MyTest // 可以作用于成员变量上
private String name; // 定义成员变量
// @MyTest // 报错!
private AnnotationDemo2() { // 构造器
}
@MyTest // 可以作用于成员方法上
public void run() { // 定义成员方法
System.out.println("飞起来!!");
}
@MyTest
public static void main(String[] args) { // main方法
// @MyTest // 报错!
int a = 10; // 定义局部变量
}
@MyTest // 可以作用于常量上
public static final String COUNTRY = "中国"; // 定义常量
}
//@MyTest // 报错!
/**
* 定义接口
*/
interface cat{
}
(2)@Retention
-
申明注解的生命周期
-
@Retention
中可使用的值定义在RetentionPolicy
枚举类中,常用值如下:值名称 作用 SOURCE 此注解只能作用在源码阶段,生成的字节码文件中不存在(一般不用) CLASS 此注解只能作用在源码、字节码文件阶段,运行阶段不存在(默认值) RUNTIME 此注解只能作用在源码、字节码、运行阶段(开发常用)
范例
总结
- 元注解是啥?
- 注解注解的注解:就是放在注解上的注解。
- 常见元注解:
- @Target:约束自定义注解可以标记的范围;
- @Retention:约束自定义注解的存活范围。
四、注解解析
- 注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
1、与注解解析相关的接口
- Annotation:注解的顶级接口,注解都是Annotation类型的对象。
- AnnotatedElement:该接口定义了与注解解析相关的解析方法。
2、解析方法
方法名称 | 说明 |
---|---|
Annotation[] getDeclaredAnnotations() | 获取当前对象上使用的所有注解,返回注解数组 |
T getDeclaredAnnotation(Class < T > annotationClass) | 根据注解类型获取对应注解对象 |
boolean isAnnotationPresent(Class < Annotation > annotationClass) | 判断当前对象是否使用了指定的注解, 如果使用了则返回true,否则返回false |
- 所有的类成分
Class、Method、Field、Constructor
,都实现了AnnotatedElement
接口,他们都拥有解析注解的能力。
3、解析注解的技巧
- 注解在哪个成分上,我们就先拿哪个成分对象。
- 比如注解作用在成员方法,则要获得该成员方法对应的Method对象, 再来拿上面的注解。
- 比如注解作用在类上,则要获得该类的Class对象,再来拿上面的注解。
- 比如注解作用在成员变量上,则要获得该成员变量的Field对象,再来拿上面的注解。
4、注解解析案例
- 需求:注解解析的案例
- 分析:
- 定义注解Book,要求如下:
- 包含属性:
- String value() 书名
- double price() 价格,默认值为100
- String[] authors() 多位作者
- 限制注解使用的范围:
- 类和成员方法上
- 指定注解的有效范围:
- RUNTIME
- 包含属性:
- 定义BookStore类,在类和成员方法上使用Book注解
- 定义AnnotationDemo01测试类获取Book注解上的数据
- 定义注解Book,要求如下:
package com.app.d9_annotation_test;
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) // 指定注解的有效范围
/**
* 1、自定义注解Book
*/
public @interface Book {
/**
* 包含属性:书名、价格(默认值为100)、多位作者
*/
String value();
double price() default 100;
String[] authors();
}
package com.app.d9_annotation_test;
// 在类上使用Book注解
@Book(value = "《射雕英雄传》", price = 155.5, authors = {"金庸", "查良镛"})
/**
* 2、定义BookStore类,在类和成员方法上使用Book注解
*/
public class BookStore {
// 在成员方法上使用Book注解
@Book(value = "《三国演义》", price = 299.9, authors = {"罗贯中", "Java"})
public void test() {
}
}
package com.app.d9_annotation_test;
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 3、定义AnnotationDemo01类,测试类获取Book注解上的数据
* 目标:完成注解的解析
*/
public class AnnotationDemo01 {
/**
* 解析类上的Book注解
*/
@Test // 声明此方法为测试方法
public void parseClass() {
// a.获取类对象
Class c = BookStore.class;
// b.判断这个类上面是否存在Book这个注解
if (c.isAnnotationPresent(Book.class)) {
// c.存在,获取该注解对象
Book book = (Book) c.getDeclaredAnnotation(Book.class);
// d.解析Book注解的数据
System.out.println("书名:" + book.value());
System.out.println("价格:" + book.price());
System.out.println("作者:" + Arrays.toString(book.authors()));
System.out.println();
}
}
/**
* 解析成员方法上的Book注解
*/
@Test
public void parseMethod() throws Exception {
// a.获取类对象
Class c = BookStore.class;
// b.获取指定的成员方法
Method m = c.getDeclaredMethod("test");
// c.判断这个成员方法上是否存在Book注解
if (m.isAnnotationPresent(Book.class)) {
// d.存在,获取该注解对象
Book book = m.getDeclaredAnnotation(Book.class);
// e.解析Book注解的数据
System.out.println("书名:" + book.value());
System.out.println("价格:" + book.price());
System.out.println("作者:" + Arrays.toString(book.authors()));
}
}
}
五、注解的应用场景一:JUnit框架
- 模拟JUnit框架
1、需求
- 定义若干个方法,只要加了MyTest注解,就可以在启动时被触发执行。
2、分析实现
- 1、自定义注解MyTest,只能注解方法,存活范围是一直都在。
- 2、定义若干个方法,只要有@MyTest注解的方法就能在启动时被触发执行,没有这个注解的方法不能执行。
package com.app.d9_annotation_test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 只能注解成员方法
@Retention(RetentionPolicy.RUNTIME) // 存活范围是一直都在
/**
* 1、自定义注解MyTest
*/
public @interface MyTest {
}
package com.app.d9_annotation_test;
import java.lang.reflect.Method;
public class AnnotationDemo2 {
/**
* 2、定义若干个方法,只要有@MyTest注解的方法就能在启动时被触发执行,没有这个注解的方法不能执行
*/
public void test1(){
System.out.println("------test1执行了-------");
}
@MyTest
public void test2() {
System.out.println("------test2执行了-------");
}
@MyTest
public void test3() {
System.out.println("------test3执行了-------");
}
public void test4() {
System.out.println("------test4执行了-------");
}
/**
* 启动菜单:有MyTest注解的才能调用
*/
public static void main(String[] args) throws Exception {
// a.创建AnnotationDemo2对象
AnnotationDemo2 ad = new AnnotationDemo2();
// b.获取类对象
Class c = AnnotationDemo2.class;
// c.获取该类对象的所有成员方法
Method[] methods = c.getDeclaredMethods();
// b.遍历methods里的成员方法
for (Method method : methods) {
// c.判断该方法是否有MyTest注解
if (method.isAnnotationPresent(MyTest.class)) {
// d.该方法有MyTest注解,使用反射技术运行该方法
method.invoke(ad);
}
}
}
}