java高级之注解

java高级之注解

注解的概念:

注解是JDK1.5的新特性。

  • 注解相当一种标记,是类的组成部分,可以给类携带一些额外的信息。
  • 注解可以加在包,类,字段,方法,方法参数以及局部变量上。
  • 注解是给编译器看的,编译器可以根据注解来完成对应的功能。

注解的作用:给程序带入参数。

注解的定义格式:

	修饰符 @interface 注解名{
         属性
    }

注解属性的定义格式:

  • 格式1:数据类型 属性名(); 没有默认值的,使用时必须赋值。
  • 格式2:数据类型 属性名() default 默认值; 有默认值的,使用时如果不赋值就会使用默认值,如果赋值就会覆盖掉默认值。

注解中能够定义什么类型的属性:

  • 8种基本数据类型。
  • String类型。
  • Class类型。
  • enum类型。
  • 枚举类型。
  • 以及上面所有的一维数组类型。

自定义注解示例代码:

public @interface MyAnnotation {
    //String类型属性
    String name();

    //int类型属性
    int age() default 20;

    //String数组类型属性
    String[] works();
}

注解使用的注意事项:

  1. 空注解可以直接使用。
  2. 一个对象中不能连续使用同一个注解多次,但是一个对象中可以使用多个不同的注解。
    (不同的位置可以使用一样的注解,但是同样的位置不能使用一样的注解)
  3. 使用带有属性注解的时候,注解中的属性一定要赋值,如果有多个属性,用(逗号)隔开,如果注解中的属性有数组,那么如果数组只有一个元素值,那么{}不用写,反之用写。
  4. 如果注解中的属性值有默认值,那么我们不必要写,也不用重新赋值,反之必须写上。
  5. 如果注解中只有一个属性,并且属性名叫value,那么使用注解的时候,属性名不用写。

注解的解析:

注解的解析就是获取注解中的属性值。

需要用到的接口:

  • AnnotatedElement

需要用到的方法:

  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该对象上是否存在指定的注解,传递的参数是 注解.class
  • Annotation getAnnotation(Class annotationClass):返回该注解对象,传递什么类型,返回什么类型,传递的参数是 注解.class

代码演示:


//自定义注解代码,上面这两个元注解后面会说到
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
    //String类型属性
    String name();

    //int类型属性
    int age() default 20;

    //String数组类型属性
    String[] works();
}


//测试代码
//使用注解并赋值参数,有默认值的可以不赋值,如果本身有默认值,再次赋值会覆盖掉默认值
@MyAnnotation(name="猪皮",works={"写代码","测试代码"})
public class Test {
    public static void main(String[] args)throws Exception {
        //获取当前类对象的Class对象
        Class c = Class.forName("cn.it.Work1.Test");

        //判断当前类对象上是否有该注解
        if(c.isAnnotationPresent(MyAnnotation.class)){
            //如果有就获取当前注解对象
            MyAnnotation m = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
            //输出属性内容
            System.out.println(m.name());
            System.out.println(m.age());
            //遍历数组元素
            for (String work : m.works()) {
                System.out.println(work);
            }
        }
    }
}

测试结果:

猪皮
20
写代码
测试代码

元注解:

元注解的作用:

  • 控制自定义的注解可以写在哪里,(类,方法,变量上,包…)
  • 控制自定义注解的生命周期

元注解1:@Target (作用域)

@Target 作用: 指定注解可出现的位置。

  1. 点进@Target底层发现,底层是 ElementType[] value(); 数组,可以赋值多个。
  2. 点进ElementType底层发现,底层的 ElementType的数据类型,是枚举,枚举的属性,都是静态修饰,直接类名调用。

ElementType中的属性:

  • TYPE:注解可以写在类上
    
  •  FIELD:注解可以写在成员变量上
    
  •  METHOD:注解可以写在方法上
    
  •  PARAMETER:注解可以写在方法参数上
    
  •  CONSTRUCTOR:注解可以写在构造方法上
    

注意:如果自定义注解的时候,不加该注解 (@Target) 那么默认就是在什么上面都可以使用。

元注解2:@Retention (生命周期)

@Retention 作用:指定注解的声明周期。
1.点到Retention底层发现,底层的 RetentionPolicy value(); 不是数组,只能赋值一个。
2.点到RetentionPolicy底层发现,底层的 RetentionPolicy数据类型是枚举,枚举的属性,都是静态修饰,直接类名调用。

RetentionPolicy中的属性:

  • SOURCE(默认级别) 注解仅存在于源码中的java文件中(不在class文件中,也不在方法区中)。
  • CLASS 注解存在于编译后的class文件中->Class文件中出现了,方法区中没有。
  • RUNTIME 注解存在于运行时期的内存中–>方法区中出现了,一旦在方法区中出现了,我们才能利用反射获取到注解。

案例演示:

要求:模仿Junit中的@Test,让带有该注解的方法执行。


//定义一个空注解 MyTest
@Retention(RetentionPolicy.RUNTIME)  //生命周期是运行时期生命周期
@Target(ElementType.METHOD)  //只能作用在方法上
public @interface MyTest {
}



//测试
public class Work2 {
    public static void main(String[] args)throws Exception {
    	//获取Work2的Class类对象
        Class c = Work2.class;
        //根据Class类对象获取所有public方法
        Method[] methods = c.getMethods();
        //根据Class对象获取当前类对象
        Object o = c.newInstance();

		//循环遍历所有的方法
        for (Method method : methods) {
        	//判断如果存在 @MyTest 注解就返回 true
            boolean flag = method.isAnnotationPresent(MyTest.class);
            if (flag){
            	//执行方法
                method.invoke(o);
            }
        }
    }

    @MyTest
    public void method1(){
        System.out.println("我是method1");
    }
    @MyTest
    public void method2(){
        System.out.println("我是method2");
    }

    public void method3(){
        System.out.println("我是method3");
    }
}

测试结果:带 @MyTest 注解的方法都被执行了。

我是method1
我是method2

注解解析案例:

//指的是作用在类上和方法上,如果不配置的话默认是在任何地方都可以使用
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)  //存在于运行时期的内存中
public @interface MyPerson {
    String name();

    //有默认值的
    float salary() default 2500.f;

    int[] number();
}


//如果给数组赋值时,只有一个值不需要{},如果两个值要加上{}
@MyPerson(name = "猪皮",number = {1,2})
public class MyPersonTest {
    public static void main(String[] args)throws Exception {
        System.out.println("=============使用当前类对象对注解进行解析=============");
        //获取类的Class对象
        Class c = MyPersonTest.class;
        //判断该类上是否包含指定注解
        boolean flag = c.isAnnotationPresent(MyPerson.class);

        if (flag) {
            //如果有该注解,就获取该注解对象,并且强转成该类型
            MyPerson mp = (MyPerson) c.getAnnotation(MyPerson.class);

            //输出属性
            System.out.println(mp.name());
            System.out.println(mp.salary());
            for (int i : mp.number()) {
                System.out.println(i);
            }
        }

        System.out.println("=============使用Method对象对注解进行解析=============");

        method();
    }

    @MyPerson(name = "奥哥",salary = 3000.f,number = 5)
    public static void method() throws Exception{
        //获取类的Class对象
        Class c = MyPersonTest.class;
        //获取方法的Class对象
        Method method = c.getMethod("method");

        //判断***方法***上是否包含MyPerson注解
        if (method.isAnnotationPresent(MyPerson.class)){
            //获取MyPerson注解中的属性
            MyPerson mp = method.getAnnotation(MyPerson.class);

            //输出属性
            System.out.println(mp.name());
            System.out.println(mp.salary());
            for (int i : mp.number()) {
                System.out.println(i);
            }
        }
    }
}

测试结果:使用不同的对象,解析不同作用域的注解

=============使用当前类对象对注解进行解析=============
猪皮
2500.0
1
2
=============使用Method对象对注解进行解析=============
奥哥
3000.0
5

如果有缺少的地方,请各位指点,我在加上。

发布了41 篇原创文章 · 获赞 48 · 访问量 4920

猜你喜欢

转载自blog.csdn.net/weixin_45216092/article/details/105297692