java 注解的使用 , 自己写注解自己用可以省下很多事 | Java Debug 笔记

767 阅读10分钟

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看<活动链接>

前言

  • 我们在Java开发中经常遇到注解的存在。比如实现继承一个类需要复写来实现。注解的实现也很简单。简单中也会出现错误。使用不慎可能也会出现bug的

spring开源框架,里面全是通过注解实现的,我们使用在使用的时候也尝到不少好处,所以就抽空看看Java给我们提供的注解机制的使用。


什么是注解

  • 注解就是类似于注释一样,起到对类、方法、字段等的说明作用。Annotation(注解)是在jdk1.5之后才引入的概念。所以你要想实现注解你的jdk必须1.5以上。他可以用于创建文档,跟踪代码的依赖性。甚至也可以执行基本的编译语法检查。注解的格式是以@开头的,@interface形式的类名申请。上面说了,注解你就可以理解为注释,因为注解是不会影响到代码的语义的。注解有三种存在方式,一种是存在源码中,一种是在编译中,一种是在运行期间有效。

注解的作用

  • 编写文档:通过代码里标识的元数据生成文档。

  • 代码分析:通过代码里标识的元数据对代码进行分析。

  • 编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查

基本注解

  • 在Java中我们初学者可能不知道什么是注解,但是我们肯定都是永富哦注解的,下面来看看我们在哪里用过他们吧,

Override

  • java.lang.Override是一个标记类型注解,它被用作标注方法.他说明了该方法是继承了父类的方法,就是重写了父类中同名方法。如果我们在一个方法上面使用的@override,但是实际上父类根本没有这个方法,就是说我们这个方法没有重载父类的方法。这个时候我们在编译时就会报相应的编译错误。

SuppressWarnings

  • 我对这个注解的理解就是通过它告诉编译器忽视我下面可能出现的警告。实际上里面有很多参数,不同参数设置不同的警告
-  deprecation,使用了过时的类或方法时的警告
-  unchecked,执行了未检查的转换时的警告
-  fallthrough,当 switch 程序块直接通往下一种情况而没有 break 时的警告
-  path,在类路径、源文件路径等中有不存在的路径时的警告
-  serial,当在可序列化的类上缺少serialVersionUID 定义时的警告
-  finally ,任何 finally 子句不能正常完成时的警告
-  all,关于以上所有情况的警告

Deprecated

  • 这个注解我实际没有接触过,这个我在网上搜了一下,人家的解释为

它的作用是对不应该再使用的方法添加注解,当编程人员使用这些方法时,将会在编译时显示提示信息,它与javadoc里的@deprecated标记有相同的功能,准确的说,它还不如javadoc @deprecated,因为它不支持参数,使用@Deprecated的示例代码示例如下: 这里写图片描述

  • 在我们平时编写代码用到jdk中的方法,有的时候回遇到某个方法过时了,但是可以用,那个方法就是通过这个注解实现的,简单说就是提示开发者方法过时,尽量避免使用过时方法。

jdk提供了开发注解的方法

  • 有了上面的介绍我们多多少少对注解有了了解,既然Java已经有了注解的概念那么我们如何开发自己的注解呢。在java.lang.annotation这个包里jdk放置了关于注解的注解,就是通过里面的注解类我们可以开发自己的注解。下面先来认识一下注解的注解吧。

这里写图片描述

  • 通过观察源码,其中annotation 、annotationFormatError、annotationTypeMismatchExcetion、IncompleteAnnotationException这四个类有事异常类又是基本类页游格式化的,这些类我们开发注解时我们不会遇到的,都是jdk里逻辑。我们真正使用的是剩下的几个类。

ElementType

  • 这个类是个枚举,这里枚举的字段就是给Target类中使用的。具体在Target中说明 RetentionPolicy

  • 这个类是个枚举,这里枚举的字段就是给Retention类中使用的。具体在Retention中说明 Documented

/**
 * Indicates that annotations with a type are to be documented by javadoc
 * and similar tools by default.  This type should be used to annotate the
 * declarations of types whose annotations affect the use of annotated
 * elements by their clients.  If a type declaration is annotated with
 * Documented, its annotations become part of the public API
 * of the annotated elements.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
  • 这是官网的jdk上对documented类的描述,这段描述的大致意思是该类的申明是用在生成Java文档上的注解,也就是说这个注解上就可以生成文档说明,并且这个也是默认的实用工具。这种注解应该在客户端上被注入。与此同时他们的方法也成为公共的API.

Target

/**
 * Indicates the kinds of program element to which an annotation type
 * is applicable.  If a Target meta-annotation is not present on an
 * annotation type declaration, the declared type may be used on any
 * program element.  If such a meta-annotation is present, the compiler
 * will enforce the specified usage restriction.
 *
 * For example, this meta-annotation indicates that the declared type is
 * itself a meta-annotation type.  It can only be used on annotation type
 * declarations:
 * <pre>
 *    &#064;Target(ElementType.ANNOTATION_TYPE)
 *    public &#064;interface MetaAnnotationType {
 *        ...
 *    }
 * </pre>
 * This meta-annotation indicates that the declared type is intended solely
 * for use as a member type in complex annotation type declarations.  It
 * cannot be used to annotate anything directly:
 * <pre>
 *    &#064;Target({})
 *    public &#064;interface MemberType {
 *        ...
 *    }
 * </pre>
 * It is a compile-time error for a single ElementType constant to
 * appear more than once in a Target annotation.  For example, the
 * following meta-annotation is illegal:
 * <pre>
 *    &#064;Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
 *    public &#064;interface Bogus {
 *        ...
 *    }
 * </pre>
 */
  • 同样上面仍然是Java JDK官网的说明,首先是说如果target指定的类型元素不存在的话,那么通过target指定的注解将可以用在任何地方,如果target指定元素,则该注解必须只能用在指定的元素上,target指定的元素是通过ElementType这个枚举类指定的,里面的枚举就是target可以指定的值。官网上的列子是Target(ElementType.ANNOTATION_TYPE)表明这个注解只能用在注解本省上面。还有一种属性不在ElementType枚举中,就是target({}),就是通过{}的注解是用来在负责注解上的成员变量,他不能用在其他的地方。

  • ElementType.CONSTRUCTOR:用于描述构造器

  • ElementType.FIELD:成员变量、对象、属性(包括enum实例)

  • ElementType.LOCAL_VARIABLE:用于描述局部变量

  • ElementType.METHOD:用于描述方法

  • ElementType.PACKAGE:用于描述包

  • ElementType.PARAMETER:用于描述参数

  • ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明

Retention

/**
 * Indicates how long annotations with the annotated type are to
 * be retained.  If no Retention annotation is present on
 * an annotation type declaration, the retention policy defaults to
 * {@code RetentionPolicy.CLASS}.
 *
 * <p>A Retention meta-annotation has effect only if the
 * meta-annotated type is used directly for annotation.  It has no
 * effect if the meta-annotated type is used as a member type in
 * another annotation type.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
  • 这个类的属性是通过RetentionPolicy这个枚举类列举属性的,这个主要的作用是指定我们的注解存在的时间或者说是存在的时刻。这个属性只有直接用在原注释类型的注解上才有效。如果用在其他注解的成员变量上就没有效果了。

  • RetentionPolicy.SOURCE : 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。

  • RetentionPolicy.CLASS : 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式

- RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。

Inherited

/**
 * Indicates that an annotation type is automatically inherited.  If
 * an Inherited meta-annotation is present on an annotation type
 * declaration, and the user queries the annotation type on a class
 * declaration, and the class declaration has no annotation for this type,
 * then the class's superclass will automatically be queried for the
 * annotation type.  This process will be repeated until an annotation for this
 * type is found, or the top of the class hierarchy (Object)
 * is reached.  If no superclass has an annotation for this type, then
 * the query will indicate that the class in question has no such annotation.
 *
 * <p>Note that this meta-annotation type has no effect if the annotated
 * type is used to annotate anything other than a class.  Note also
 * that this meta-annotation only causes annotations to be inherited
 * from superclasses; annotations on implemented interfaces have no
 * effect.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
  • 一句话总结就是是否允许子类继承该注解

自定义注解

  • 上面说了这么多,那么下面我们开始开发自己的注解吧。

构造器注解

/**
 * 该注解用于doc文档
 * 该注解用于类的构造函数的注解
 * 该注解尽在运行期间有效
 * @author xinhua
 *
 */
@Documented
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConstructorAnno
{

    String decs() default "我是描述构造函数的注解";
}

字段注解

/**
 * 该注解用于生成doc文档
 * 该注解用于在类的字段属性上的注解
 * 该注解尽在运行期间有效
 * @author xinhua
 *
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnno
{

    String desc() default "我是描述字段的注解";
}

局部变量注解

/**
 * 该注解用于生成doc文档
 * 该注解用于在局部变量上的注解
 * 该注解尽在运行期间有效
 * @author xinhua
 *
 */
@Documented
@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.RUNTIME)
public @interface LocalAnno
{
    String desc() default "我是描述一个局部变量的注解";
}

方法注解

/**
 * 这个注解用于生成doc文档
 * 这个注解用于类的方法上的注解
 * 这个注解尽在运行期间有效
 * @author xinhua
 *
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnno
{
    //默认提交时间  2017-2-28
    String time() default "2017-2-28";
    
    //默认  男    true 男  false 女 
    boolean sex() default true;
}

包注解

/**
 * 该注解用于生成doc文档
 * 该注解用于在包的注解
 * 该注解尽在运行期间有效
 * @author xinhua
 *
 */
@Documented
@Target(ElementType.PACKAGE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PackageAnno
{

    String desc() default "我是描述包的注解";
}

参数注解

/**
 * 该注解用于doc文档的生成
 * 该注解用于类的参数中
 * 该注解尽在运行期间有效
 * @author xinhua
 *
 */
@Documented
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface StuInfo
{
    String value() default "";
}

类型注解

/**
 * 该注解用于生活曾doc文档
 * 该注解用于在类的注解
 * 该注解尽在运行期间有效
 * @author xinhua
 *
 */
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeAnno
{

    String desc() default "我是描述类型的注解";
}

加入测试的实体类

/**
 * 测试所有类型的注解的作用
 * @author xinhua
 *
 */
public @TypeAnno(desc="我是普通类")class User
{
    @FieldAnno
    private String UID;
    
    @FieldAnno(desc="我是UserName的")
    private String userName;
    
    @ConstructorAnno
    public User(){
        
    }
    
    @MethodAnno(time="2015-12-8" , sex=false)
    public void doHomeWork(@StuInfo(value="211311084") String UID, @StuInfo(value="张新华")String UserName){
         @LocalAnno(desc="flag的局部变量")boolean flag;
         
    }
}

加入测试的接口类

public interface UserImp
{

    public void DoHouseWork(@StuInfo(value="我是小学生")String stuType,@StuInfo(value="我在大扫除")String things);
}

测试注解

/**
 * 开始通过反射获取注解内容
 * @author xinhua
 *
 */
public class Junit
{

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException
    {
        Class<?> userClass = Class.forName("tom.change.annotation.User");
        //实例化
        Object userObject = userClass.newInstance();
        //1--获取最外层的就是类的注解  TypeAnno
        System.out.println(".......................类注解.......................");
        TypeAnno typeAnno= userClass.getAnnotation(TypeAnno.class);
        System.out.println(typeAnno.desc());
        //2--获取字段注解 首先获取通过反射获取类中的属性字段
        System.out.println(".......................字段注解.......................");
        Field[] fields = userClass.getDeclaredFields();
        for (Field field : fields)
        {
            FieldAnno fieldAnno = field.getAnnotation(FieldAnno.class);
            System.out.println(fieldAnno.desc());
        }
        //3-- 方法的注解
        System.out.println(".......................方法注解.......................");
        Method method = userClass.getMethod("doHomeWork", String.class,String.class);
        MethodAnno methodAnno = method.getAnnotation(MethodAnno.class);
        System.out.println(methodAnno.time()+"@@@"+methodAnno.sex());
        
        //4-- 参数的注解  动态代理方式实现参数
        System.out.println(".......................参数注解.......................");
        UserImp userImp=getMethodParameter(UserImp.class);
        userImp.DoHouseWork("张新华", "--》扫地");
        
        //5--局部变量注解 
        System.out.println(".......................局部变量注解.......................");
        //赞未获得到
    }
    
    public static<T> T getMethodParameter(Class<T> target){
        return (T)Proxy.newProxyInstance(target.getClassLoader(), new Class<?>[]{target}, new InvocationHandler()
        {
            
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable
            {
                Annotation[][] parameterAnnotations = method.getParameterAnnotations();
                for (int i=0;i<parameterAnnotations.length;i++)
                {
                    Annotation[] annotations=parameterAnnotations[i];
                    StuInfo stuInfo =(StuInfo) annotations[0];
                    System.out.println(stuInfo.value()+"@@@"+args[i]);
                }
                
                return null;
            }
        });
    }
}

效果图

这里写图片描述