java 核心技术Ⅱ--章八:注解

注解

注解是那些插入到源代码中使用其他工具可以对其进行处理的标签,其不会改变程序的编译方式,只是对源程序的解读,也就是说源代码正常执行,我们会在特殊的地方对注解进行解读。两部分是分开进行的。

1、定义注解

//定义TestMyAnnotation 注解
public @interface TestMyAnnotation {

}
1.1、元注解

元注解其实就是注解的注解,用来解释你所定义的注解怎么用,用在什么地方,下面就来详细解释元注解。

元注解:@Retention@Documented@Target@Inherited@Repeatable(jdk8新加入的)共5种。

@Retention: 当这个元注解应用在一个自定义注解时,用来指明自定义注解可以保留多久。

它的取值如下:

  • RetentionPolicy.SOURCE 不包括在类文件中的注解(用来做源代码层级的提示作用)。
  • RetentionPolicy.CLASS(默认值) 包括在类文件中的注解,但是虚拟机不需要将他们载入(用来做编译的校验)。
  • RetentionPolicy.RUNTIME 包括在类文件中的注解,并由虚拟机载入,可以通过反射来得到它们(springMVC等框架常用)。
//定义TestMyAnnotation 注解
@Retention(RetentionPolicy.RUNTIME)
public @interface TestMyAnnotation {

}

上述自定义注解可以载入虚拟机并通过反射得到内容,后续我们会讲如何操作。

@Documented:当这个元注解应用在一个自定义注解时,该自定义注解会归档,用这个注解注释的方法等,在用javadoc归档时,会随着方法一起进行归档。

@Target:当这个元注解应用在一个自定义注解时,用来指明自定义注解可以可以作用的地方。

  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
  • ElementType.CONSTRUCTOR 可以给构造方法进行注解
  • ElementType.FIELD 可以给属性进行注解
  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
  • ElementType.METHOD 可以给方法进行注解
  • ElementType.PACKAGE 可以给一个包进行注解
  • ElementType.PARAMETER 可以给一个方法内的参数进行注解
  • ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举。

@Inherited:当这个元注解应用在一个自定义注解时,该自定义注解注解了一个父类,如果它的子类没有应用任何注解,那么它的子类及孙类也拥有这一注解。

//定义TestMyAnnotation 注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TestMyAnnotation {

}

@TestMyAnnotation
class A {}

//子类未应用任何注解,自动应用@TestMyAnnotation注解
class B extends A{}

@Repeatable:当这个元注解应用在一个自定义注解时,该自定义注解可重复使用。例如一个人既是老师又是父亲。

@interface Persons {
    Person [] value();
}

@Repeatable(Persons.class)
@interface Person{
    String role() default "";
}

@Person(role="teacher")
@Person(role="father")
class Man{

}

注意:@Repeatable里面需要一个容器,它里面需要存放其他的注解,该容器注解里面必须有一个属性 value且类型为被@Repeatable注解过的注解数组

1.2、注解属性

注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

注解属性的类型有以下几种:

  • 基本类型(int、short、long、byte、char、double、float、boolean)
  • String类型
  • Class类型
  • enum(枚举)类型
  • 注解类型(@interface)
  • 由前面所述类型组成的数组类型。

一个简单的例子:

@interface Persons {
    Person [] value();
}
@interface Person{
    enum Status {BUSY,FREE}
    String role() default "";
    int age() default 10;
    String[] duty() default {"teach","learn","work"};
    Class<?> test() default Void.class;
    TestMyAnnotation ref() default @TestMyAnnotation;
    Status status() default Status.BUSY;
}

//未写明的属性使用默认属性
@Person()
@Person(role="father",age=20,duty={"raise"})
class Man{  
}

若注解中仅仅只有value这一个属性时,应用这个注解时可以直接将属性值填入括号中

@interface Person{
    int value() default 0;
}
//直接写入属性值 即为value属性的值
@Person(10)
class Man{
}

若注解中没有任何属性或属性均有默认值时,则应用注解时,不必使用括号

@interface Test{
}

@Test
class Man{
}

2、jdk自带注解

注解 应用场合 目的
@Deprecated 全部 将项标记为过时的
@SuppressWarnings 除了包和注解之外的所有情况 阻止某个给定类型的警告消息
@SafeVarargs 方法和构造器 断言varargs参数可安全使用
@Override 方法 检查该方法是否覆盖了一个超类方法
@FunctionalInterface 接口 将接口标记为只有一个抽象方法的函数式接口
@PostConstruct 方法 被标记的方法应该在构造之后被调用
@PreDestroy 方法 被标记的方法应该在对象被移除之前调用
@Resource 类、接口、方法、域 在类或接口上:标记为其他地方要用到的资源。 在方法或域上:为“注入”而标记
@Resources 类、接口 一个资源数组
@Generated 全部 供代码生成工具来使用

@Deprecated:该注解添加到任何不在鼓励的使用项上。会产生横线的效果。

这里写图片描述

@Override:这个注解应该会常用,当覆盖父类的方法时,方法前会有此注释。

public class Test {

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // TODO Auto-generated method stub
        return super.clone();
    }
}

@PostConstruct:被标记的方法应该在构造之后被调用

@PreDestroy:被标记的方法应该在对象被移除之前调用

这两个注解是一种提议,如果你不遵守也不会报错,但在javaEE5 Servlet 和 spring框架中这两个注解声明的方法被强制调用。

3、利用反射获得注解内容

package com.tjy.learn;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

@Target(ElementType.TYPE) //该注解可以用在接口、类、枚举
@Retention(RetentionPolicy.RUNTIME) //保留到运行期间
@interface TestClass {
    String value() default "com.tjy.learn.Test";
}

@Target(ElementType.METHOD) //该注解可以用在方法
@Retention(RetentionPolicy.RUNTIME) //保留到运行期间
@interface TestMethod{
    //方法名
    String name() default "";
    //返回类型
    Class<?> type() default Void.class;
    //参数个数
    int args() default 0;
}

@Target(ElementType.FIELD)//该注解可以用在属性
@Retention(RetentionPolicy.RUNTIME) //保留到运行期间
@interface TestFiled{
    String name() default "";
}

@TestClass("com.tjy.learn.myClass")
class myClass{

    @TestFiled(name="id")
    int id;

    @TestFiled(name="name")
    String name;

    @TestFiled
    String value;

    @TestFiled
    Test test;

    @TestMethod(name="getId",type=Integer.class,args=0)
    public int getId() {
        return id;
    }

    @TestMethod
    public void setId(int id) {
        this.id = id;
    }

    @TestMethod(name="getName",type=String.class,args=0)
    public String getName() {
        return name;
    }

    @TestMethod
    public void setName(String name) {
        this.name = name;
    }

    @TestMethod(name="getValue",type=String.class,args=0)
    public String getValue() {
        return value;
    }

    @TestMethod(name="setValue",type=Void.class,args=1)
    public void setValue(String value) {
        this.value = value;
    }

    @TestMethod
    public Test getTest() {
        return test;
    }

    @TestMethod(name="setTest",type=Void.class,args=1)
    public void setTest(Test test) {
        this.test = test;
    }
}

public class TestMyAnnotation {

    public static void main(String[] args) throws ClassNotFoundException {
        //获得bean的全路径名称
        String className = "com.tjy.learn.myClass";
        //通过反射获得类
        Class clazz = Class.forName(className);
        //判断该类上是否有注解@TestClass
        boolean hasAnnotation = clazz.isAnnotationPresent(TestClass.class);
        if(hasAnnotation){
            TestClass testClass = (TestClass) clazz.getAnnotation(TestClass.class);
            System.out.println("进行类:"+className+"分析,类路径:"+testClass.value());
        }
        //获取类的成员变量
        Field [] fields = clazz.getDeclaredFields();
        for(Field f:fields){
            if(f.isAnnotationPresent(TestFiled.class)){
                //设置属性--反射可访问
                f.setAccessible(true);
                TestFiled testFiled = f.getAnnotation(TestFiled.class);
                System.out.println("进行属性:"+f.getName()+"分析,该属性名字为:"+testFiled.name());
            }
        }
        //获取类的成员变量
            Method [] Methods = clazz.getMethods();
                for(Method m:Methods){
                    if(m.isAnnotationPresent(TestMethod.class)){
                        TestMethod testMethod = m.getAnnotation(TestMethod.class);
                        System.out.println("进行方法:"+m.getName()+"分析,该属性名字为:"+testMethod.name()+" 返回类型为"+testMethod.type().getName()+" 参数个数:"+testMethod.args());
                    }
                }
    }
}

输出结果:

进行类:com.tjy.learn.myClass分析,类路径:com.tjy.learn.myClass
进行属性:id分析,该属性名字为:id
进行属性:name分析,该属性名字为:name
进行属性:value分析,该属性名字为:
进行属性:test分析,该属性名字为:
进行方法:getName分析,该属性名字为:getName 返回类型为java.lang.String 参数个数:0
进行方法:getValue分析,该属性名字为:getValue 返回类型为java.lang.String 参数个数:0
进行方法:getId分析,该属性名字为:getId 返回类型为java.lang.Integer 参数个数:0
进行方法:setName分析,该属性名字为: 返回类型为java.lang.Void 参数个数:0
进行方法:setValue分析,该属性名字为:setValue 返回类型为java.lang.Void 参数个数:1
进行方法:setId分析,该属性名字为: 返回类型为java.lang.Void 参数个数:0
进行方法:getTest分析,该属性名字为: 返回类型为java.lang.Void 参数个数:0
进行方法:setTest分析,该属性名字为:setTest 返回类型为java.lang.Void 参数个数:1

猜你喜欢

转载自blog.csdn.net/tjy_521/article/details/80611462
今日推荐