注解概述
- 注解是JDK1.5的新特性。
- 标记(注解)可以加在包,类,字段,方法,方法参数以及局部变量上。
- 注解是给编译器或JVM看的,编译器或JVM可以根据注解来完成对应的功能。
注解的作用:
-
使用javadoc生成帮助文档:里边可以包含注解**@author和@version**
-
编译检查:@Override @FunctionalInterface
-
框架的配置(框架=代码+配置)
自定义注解
定义一个没有属性的注解
格式:
public @interface 注解名{ }
注意:
-
注解使用的也是.java文件,编译生成的也是.class文件
-
注解和类和接口和枚举都是同一个层次的,都是一种数据类型
定义一个有属性的注解
注解中没有成员变量,也没有成员方法
注解中可以包含属性,属性看成和抽象方法一个格式,但是可以包含默认值
定义格式:
public @interface 注解名{
修饰符 数据类型 属性名();
修饰符 数据类型 属性名() default 默认值;
}
注意:
-
注解的属性修饰符省略不写则默认为 public abstract ;建议写出,增强语句的可读性
-
注解属性的数据类型:
a.基本数据类型(4类8种):byte,short,int,long,float,double,char,boolean
b.引用数据类型:String类型,反射类型,注解类型,枚举类型
c.以及以上所有类型的一维数组
定义包含特殊属性value的注解:注解中只有一个属性,并且叫value;或者注解中有其他的属性,但是属性必须有默认值
示例
public @interface MyAnno02 {
//定义一个int类型的属性
//public abstract int a();
//abstract int a();
int a();
//定义一个double类型的属性,给属性添加默认值8.8
public abstract double d() default 8.8;
//定义一个String类型的数组属性
public abstract String[] arr();
//定义反射类型的属性(了解)
public abstract Class clazz();
//定义注解类型的属性(了解)
public abstract MyAnno01 my01();
//定义枚举类型的属性(了解)
public abstract Color c();
}
/* 定义包含特殊属性value的注解 */
public @interface MyAnno03 {
public abstract String value();
public abstract int aaa() default 10;
}
使用自定义注解
注解可以使用的位置:包上,类上|接口上,成员变量上,成员方法上,构造方法上,局部变量上,方法的的参数上…
注意:
-
同一个位置,同名的注解只能使用一次
-
不同的位置,同名的注解可以多次使用
注解的使用格式:
-
没有属性的注解,通过 @注解名 可以直接使用。例如:@MyAnno01
-
有属性的注解:必须使用键值对的方式,给注解中所有的属性都赋值之后,才能使用注解
格式:
@注解名(属性名=属性值,属性名=属性值,属性名=属性值,…属性名=属性值)
注:
a.有默认值的属性,可以不同赋值,使用默认值
b.给多个属性赋值,中间要使用逗号隔开
c.属性的数据类型是数组,属性的值需要使用 { } 包裹起来,说明这一组值是一个数组的属性值
数组只有一个值,可以省略 { }
示例:arr = {“a”,“b”,“c”} arr = {“a”} ==> arr = “a”
d.注解中只有一个属性,并且叫 value;或者注解中有其他的属性,但是属性均有默认值
那么我们在使用注解的时候,给属性赋值,可以省略属性名,直接写属性值
示例:(任意的数据类型)value=“aaa” ==> “aaa”
示例
@MyAnno01 // 没有属性的注解
@MyAnno02(a = 10, arr={"aaa","bbb","ccc"}) // 有多个属性的注解
public class UseMyAnno {
@MyAnno01
@MyAnno02(a=100, d=1.1, arr="aaa") // arr属性的数据类型是数组,只有一个值的情况,可省略 {}
private String name;
@MyAnno01
@MyAnno03(value="aaa")
public UseMyAnno() {
}
@MyAnno01
@MyAnno03("www") // 可省略属性名的注解
public UseMyAnno(String name) {
this.name = name;
}
@MyAnno01
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
元注解
元注解:java已经定义好的注解,可以使用元注解修饰自定义的注解
1.java.lang.annotation.Target
作用:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置。
属性:
-
ElementType[] value :只有一个属性,属性名叫value(使用的时候,就可以省略属性名,直接写属性值)
-
java.lang.annotation.ElementType:是一个枚举,枚举中的变量都是常量,可以通过枚举名.变量名直接使用
ElementType枚举中的常量:
TYPE, // 类,接口 FIELD, // 成员变量 METHOD, // 成员方法 PARAMETER, // 方法参数 CONSTRUCTOR, // 构造方法 LOCAL_VARIABLE // 局部变量
2.java.lang.annotation.Retention
作用:用来标识注解的生命周期(有效范围)
属性:
-
RetentionPolicy value: 只有一个属性,属性名叫value(使用的时候,就可以省略属性名,直接写属性值)
-
java.lang.annotation.RetentionPolicy:是一个枚举,枚举中的变量都是常量,可以通过枚举名.变量名直接使用
RetentionPolicy枚举中的常量:
SOURCE, // 注解只作用在源码(.java)阶段,生成的字节码文件(.class)中不存在 CLASS, // 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值 RUNTIME // 注解作用在源码阶段,字节码文件阶段,运行阶段
示例
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//声明自定义注解Book可以使用的位置:类上|接口上,构造方法上,成员变量上
//@Target(value={ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Target({ElementType.TYPE,ElementType.CONSTRUCTOR,ElementType.FIELD})
//声明自定义注解Book:在.java文件中,在.class文件中和在内存中都有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
//书名
public abstract String value();
//价格,默认值为 100
public abstract double price() default 100;
//多位作者
public abstract String[] authors();
}
注解解析
作用:获取注解的属性值
原理:注解的解析底层使用的反射技术
java.lang.reflect.AnnotatedElement接口:在接口中定义了注解解析的方法
AnnotatedElement接口的实现类都重写了接口中的方法,都可以使用这些方法
实现类:AccessibleObject,Class,Constructor,Field,Method,Package
AnnotatedElement接口中的常用方法:
boolean isAnnotationPresent(Class<?> annotationClass) // 判断指定的对象(Class,Method...)上,是否包含指定的注解
/* 参数:
Class<?> annotationClass:判断哪个注解,就传递哪个注解的class文件对象
判断类上,方法上有没有Book注解,就需要传递Book.class
返回值:boolean
有指定的注解,返回true
没有指定的注解,返回false
*/
T getAnnotation(Class<T> annotationClass) // 获取对象(Class,Method...)上指定的注解
/* 参数:
Class<T> annotationClass:获取哪个注解,就传递哪个注解的class文件对象
获取类上,方法上的Book注解,就需要传递Book.class
返回值:
T:返回获取到的注解,获取的注解不存在,返回null
*/
Annotation[] getAnnotations() // 返回此元素上存在的所有公共注释。
Annotation[] getDeclaredAnnotations() // 返回直接存在于此元素上的所有注释,包含其他修饰符的注解
示例
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
@Book(value = "西游记",authors = {"吴承恩"})
public class Demo01ParseAnnotation {
@Book(value = "水浒传",authors = {"施耐庵","林冲"},price = 88.8)
public void method(){}
/* 解析类上的注解:获取类上注解的属性值 */
@Test
public void show01() throws ClassNotFoundException {
//1.获取当前类Demo01ParseAnnotation的class文件对象
Class clazz = Class.forName("com.itheima.demo09Annotation.Demo01ParseAnnotation");
//2.使用class文件对象中的方法isAnnotationPresent判断类上是否包含指定的Book注解
boolean b = clazz.isAnnotationPresent(Book.class);
System.out.println(b);//true
if(b){
//3.如果类上包含Book注解,使用class文件对象中的方法getAnnotation获取Book注解
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));
}
}
/* 解析方法上的注解:获取方法上注解的属性值 */
@Test
public void show02(){
//1.获取当前类Demo01ParseAnnotation的class文件对象
Class clazz = Demo01ParseAnnotation.class;
//2.使用class文件对象中的方法getMethods获取类中所有的方法,返回一个Method数组
Method[] methods = clazz.getMethods();
//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;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
//书名
public abstract String value();
//价格,默认值为 100
public abstract double price() default 100;
//多位作者
public abstract String[] authors();
}
注解和反射的综合案例
import java.lang.reflect.Method;
/*
注解和反射的综合案例
需求:
模拟Junit单元测试的@Test注解:作用可以单独的运行某一个方法
方法上添加了@Test注解,可以运行
方法上没有添加@Test注解,不可以运行
*/
public class Demo01Test {
public static void main(String[] args) throws Exception {
//3.获取测试类的class文件对象
Class clazz = Class.forName("com.itheima.demo10test.DemoMyTest");
//4.使用class文件对象中的方法newInstance实例化对象
Object obj = clazz.newInstance();
//5.使用class文件对象中的方法getMethods,获取类中所有的成员方法,返回一个Method数组
Method[] methods = clazz.getDeclaredMethods();//只会获取本类的方法,包含任意修饰符的,没有父类的方法
//6.遍历数组,获取每一个Method对象
for (Method method : methods) {
//7.使用Method对象中的方法isAnnotationPresent判断Method对象上是否有MyTest注解
boolean b = method.isAnnotationPresent(MyTest.class);
//8.如果Method对象上有MyTest注解,使用invoke运行方法
if(b){
method.invoke(obj);
}
}
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//1.定义一个注解叫MyTest,使用元注解修饰(a.只能在方法上使用,b.运行时有效)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
//2.定义一个测试类,在测试类中定义多个方法,让部分方法使用MyTest注解修饰
public class DemoMyTest {
@MyTest
public void show01(){
System.out.println("show01方法");
}
public void show02(){
System.out.println("show02方法");
}
public void show03(){
System.out.println("show03方法");
}
@MyTest
public void show04(){
System.out.println("show04方法");
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/112031.html