注解的作用:
1.使用javadoc生成帮助文档:里边可以包含注解**@author和@version**
2.编译检查:@Override @FunctionalInterface
3.框架的配置(框架=代码+配置):框架的时候讲
/*
自定义注解:定义一个没有属性的注解
格式:
public @interface 注解名{
}
注意:
注解使用的也是.java文件,编译生成的也是.class文件
注解和类和接口和枚举都同一个层次
*/
public @interface MyAnnotation01 {
}
/*
自定义注解:定义含有属性的注解
注解的属性,可以看成是抽象方法,但是有默认值
定义格式:
public @interface 注解名{
修饰符 数据类型 属性名();
修饰符 数据类型 属性名() default 默认值;
}
注意:
1.属性的修饰符固定使用public abstract,可以省略不写,不写默认也是;建议写出,可以增强语句的可读性
2.属性的数据类型:
a.基本数据类型(4类8种):byte,short,int,long,float,double,char,boolean
b.引用类型:String类型,反射类型(Class),注解类型,枚举类型
c.以及以上所有的类型的一维数组
*/
public @interface MyAnnotation02 {
//定义一个int类型的属性
public abstract int a();
//定一个double类型的属性,并给属性赋默认值8.8
public abstract double d() default 8.8;
//定义一个String数组类型的属性
public abstract String[] arr();
//定义反射类型的属性(了解)
//public abstract Class clazz();
//定义注解类型的属性(了解)
//public abstract MyAnnotation01 my01();
//定义枚举类型的属性(了解)
//public abstract Color c() default Color.RED;
}
/*
定义枚举
枚举中的成员变量都是常量
*/
public enum Color {
/*
public static final Color RED = new Color();
public static final Color GREEN = new Color();
*/
RED,GREEN
}
/*
使用自定义注解
注解可以使用的位置:
包上,类上|接口上,成员变量上,成员方法上,构造方法上,局部变量上,方法的参数上...
注意:
同一个位置,同名的注解只能使用一次
不同的位置,同名的注解可以使用多次
注解的使用格式:
1.没有属性的注解,通过@注解名可以直接使用
2.有属性的注解必须使用键值对的方式,给注解中的所有属性赋值之后,才能使用
格式:
@注解名(属性名=属性值,属性名=属性值,..属性名=属性值)
a.有默认值的属性,可以不用赋值,使用默认值
b.给多个属性赋值中间要使用逗号分隔开
c.属性是数组类型,属性值需要使用{ }包裹起来;数组只有一个值,可以省略{ }
arr = {"a","b","c"} arr={"a"}==> arr="a"
d.注解中只有一个属性,属性的名叫value,那么赋值的时候可以省略属性名,直接写属性值
或者注解有其他属性,但是必须有默认值
value=10==> 10
*/
@MyAnnotation01
@MyAnnotation02(a=10,d=1.1,arr={"a","b","c"})
public class UseMyAnnotation {
@MyAnnotation01
@MyAnnotation02(a = 100,arr="aa")
@MyAnnotation03(aaa=10,value = 20)
private String name;
private int age;
@MyAnnotation01
@MyAnnotation03(10)
public UseMyAnnotation() {
}
public UseMyAnnotation(String name, int age) {
this.name = name;
this.age = age;
}
@MyAnnotation01
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//定义只有一个属性的注解,属性名叫value
//也可以有其他的属性,但是属性必须有默认值
public @interface MyAnnotation03 {
public abstract int aaa() default 100;
public abstract int value();
}
/*
定义一个注解:Book
- 包含属性:String value() 书名
- 包含属性:double price() 价格,默认值为 100
- 包含属性:String[] authors() 多位作者
*/
public @interface Book {
//书名
public abstract String value();
//价格,默认值为 100
public abstract double price() default 100;
//多位作者
public abstract String[] authors();
}
/*
使用自定义注解Book
*/
@Book(value = "钢铁是怎样炼成的",authors = "奥斯托洛夫斯基")
public class UseBook {
@Book(value = "java编程思想",price = 88.8,authors = {"aaa","bbb"})
public String name;
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
元注解:java已经定义好的注解,可以用来修饰自定义的注解
1.@Target
作用:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置。
属性:
ElementType[] value:只有一个属性名字叫value,使用的时候,给属性赋值直接写属性值即可
java.lang.annotation.ElementType:是一个枚举,枚举中定义的变量都是常量
TYPE,类,接口
FIELD, 成员变量
METHOD, 成员方法
PARAMETER, 方法参数
CONSTRUCTOR, 构造方法
LOCAL_VARIABLE, 局部变量
2.@Retention
作用:用来标识注解的生命周期(有效范围)
属性:
RetentionPolicy value:只有一个属性名字叫value,使用的时候,给属性赋值直接写属性值即可
java.lang.annotation.RetentionPolicy:是一个枚举,枚举中定义的变量都是常量
SOURCE:注解只作用在源码阶段(.java文件中),生成的字节码文件(.class文件中)中不存在
CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段
*/
@Target({ElementType.TYPE,ElementType.METHOD})//声明自定义注解可以使用在类上和方法上
@Retention(RetentionPolicy.RUNTIME)//声明自定义注解生命周期在.java文件中.class文件中和内存中都有效
public @interface Book {
//书名
public abstract String value();
//价格,默认值为 100
public abstract double price() default 100;
//多位作者
public abstract String[] authors();
}
import com.zl.demo03Annotation.MyAnnotation01;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
/*
注解解析:
获取注解的属性值
注解解析底层使用的都是反射技术
java.lang.reflect.AnnotatedElement接口:在接口中定义了注解解析相关的方法
接口的实现类:实现类都重写了接口中的方法,都可以使用
AccessibleObject, Class, Constructor, Field, Method, Package
接口中的方法:
boolean isAnnotationPresent(Class annotationClass):判断指定的对象上(Class, Constructor, Field, Method)是否有指定的注解
参数:
Class annotationClass:判断哪个注解,就传递哪个注解的class文件对象
判断类上,方法上...有没有Book注解,就需要传递Book.class
返回值:
有指定的注解,返回true
没有指定的注解,返回false
T getAnnotation(Class annotationClass) 获取对象上(Class, Constructor, Field, Method)指定的注解
参数:
Class annotationClass:获取哪个注解,就传递哪个注解的class文件对象
获取类上,方法上...有没有Book注解,就需要传递Book.class
返回值:
T:返回指定的注解,获取不到返回null
了解:
Annotation[] getAnnotations() 获取对象上所有公共的注解
Annotation[] getDeclaredAnnotations() 获取对象上所有声明的注解
*/
@MyAnnotation01
@Book(value = "西游记",price = 66.6,authors = "吴承恩")
public class Demo01ParseAnnotation {
@Book(value = "水浒传",authors = "施耐庵")
public void method(){}
/*
解析类上的注解
实现步骤:
1.获取类的class文件对象
2.使用class文件对象中的方法isAnnotationPresent判断类上是否有指定的Book注解
3.如果类上有Book注解,使用class文件对象中的方法getAnnotation获取到Book注解
4.使用注解名.属性名()获取注解的属性值
*/
@Test
public void parseClassAnnotation() throws ClassNotFoundException {
//1.获取类的class文件对象
Class clazz = Class.forName("com.zl.demo05Annotation.Demo01ParseAnnotation");
//2.使用class文件对象中的方法isAnnotationPresent判断类上是否有指定的Book注解
boolean b = clazz.isAnnotationPresent(Book.class);
System.out.println(b);
//3.如果类上有Book注解,使用class文件对象中的方法getAnnotation获取到Book注解
if(b){
Book book = (Book)clazz.getAnnotation(Book.class);
//4.使用注解名.属性名()获取注解的属性值
String value = book.value();
System.out.println(value);
double price = book.price();
System.out.println(price);
String[] authors = book.authors();
System.out.println(Arrays.toString(authors));
}
}
/*
解析方法上的注解
实现步骤:
1.获取类的class文件对象
2.使用class文件对象中的方法getMethods获取类中所有的公共成员方法,返回一个Method数组
3.遍历Method数组,获取每一个Method对象
4.使用Method对象中的方法isAnnotationPresent判断方法上是否有Book注解
5.如果方法上有Book注解,使用Method对象中的方法getAnnotation获取Book注解
6.使用注解名.属性名()获取属性的值
*/
@Test
public void parseMethodAnnotation(){
//1.获取类的class文件对象
Class clazz = Demo01ParseAnnotation.class;
//2.使用class文件对象中的方法getDeclaredMethods获取类中所有的成员方法,返回一个Method数组
Method[] methods = clazz.getDeclaredMethods();
//3.遍历Method数组,获取每一个Method对象
for (Method method : methods) {
//4.使用Method对象中的方法isAnnotationPresent判断方法上是否有Book注解
boolean b = method.isAnnotationPresent(Book.class);
//System.out.println(method.getName()+"-->"+b);
if(b){
//5.如果方法上有Book注解,使用Method对象中的方法getAnnotation获取Book注解
Book book = method.getAnnotation(Book.class);
//6.使用注解名.属性名()获取属性的值
System.out.println(book.value());
System.out.println(book.price());
System.out.println(Arrays.toString(book.authors()));
}
}
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//1.自定义一个注解叫MyTest,使用元注解修饰MyTest注解(只能在方法上使用,运行期有效)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
//2.定义一个测试类,在测试类中定义多个方法,让部分方法使用MyTest注解
public class DemoMyTest {
public void show01(){
System.out.println("show01方法");
}
@MyTest
public void show02(){
System.out.println("show02方法");
}
@MyTest
public void show03(){
System.out.println("show03方法");
}
public void show04(){
System.out.println("show04方法");
}
@MyTest
public void show05(){
System.out.println("show05方法");
}
}
import java.lang.reflect.Method;
/*
注解和反射的综合案例
需求:
模拟Junit测试的@Test
添加了@Test注解的方法,可以运行
没有添加@Test注解的方法,不可以运行
分析:
1.自定义一个注解叫MyTest,使用元注解修饰MyTest注解(只能在方法上使用,运行期有效)
2.定义一个测试类,在测试类中定义多个方法,让部分方法使用MyTest注解
3.获取测试类的class文件对象
4.使用class文件对象中的方法newInstance实例化对象
5.使用class文件对象中的方法getMethods获取测试类中所有的成员方法,返回一个Method数组
6.遍历Method数组,获取每一个Method对象
7.使用Method对象中的方法isAnnotationPresent判断,Method上是否有指定的MyTest注解
8.如果Method对象上有MyTest注解,使用invoke方法,运行Method
*/
public class Demo01AnnotationTest {
public static void main(String[] args) throws Exception {
//3.获取测试类的class文件对象
Class clazz = Class.forName("com.zl.demo06AnnotationTest.DemoMyTest");
//4.使用class文件对象中的方法newInstance实例化对象
Object obj = clazz.newInstance();
//5.使用class文件对象中的方法getMethods获取测试类中所有的成员方法,返回一个Method数组
Method[] methods = clazz.getDeclaredMethods();
//6.遍历Method数组,获取每一个Method对象
for (Method method : methods) {
//7.使用Method对象中的方法isAnnotationPresent判断,Method上是否有指定的MyTest注解
if(method.isAnnotationPresent(MyTest.class)){
//8.如果Method对象上有MyTest注解,使用invoke方法,运行Method
method.invoke(obj);
}
}
}
}