zoukankan      html  css  js  c++  java
  • Java注解详解

    一、介绍

    1 概念

    注解(Annotation),也叫元数据。一种代码级别的说明。它是Java 5新增的技术。要区别注释,注解是代码里的一种特殊标记,可在编译前、编译后、运行时等不同的时期被读取,并作出相应的处理。

    2 原则

    由于注解的代码是附属信息,它要遵循一个基本原则:注解不能直接干扰代码的运行,无论增加或者删除注解,代码都能正常的运行。

    二、如何定义注解

    1 创建

    定义新的注解使用@interface关键字

    public @interface MyAnnotation {
    }
    

    1.1 成员变量

    Annotation只有成员变量。其成员变量以“无形参的方法”形式来声明。例:

    public @interface MyAnnotation {
         //String定义了成员类型,name定义了成员名
        String name();
        //也可以在定义成员变量时指定默认值
        int age() default 18;
    }
    

    该注解如何使用呢?

    class AnnoTest{
        //age已经为其指定默认值,那么在使用的时候就可以不为它指定值,age就会使用这个指定的默认值
        @MyAnnotation(name = "张三")
        public void test() {
        }
    }
    

    若是Annotation只有一个成员变量,定义是该成员名必须为value

    public @interface MyAnnotation {
        String value();
    }
    

    该注解使用时也可以省略成员名和“=”号,例:

    class AnnoTest{
        @MyAnnotation( "张三")
        public void test() {
        }
    }
    

    根据Annotation包含的成员数目可以将其分为两类:

    1. 标记注解:没有成员变量,如@Override
    2. 元数据注解:包含成员变量的注解,如@SuppressWarnings

    1.2 元注解

    我们自定义注解时可以使用jdk自带的4个元注解来修饰定义的注解

    1. @Retention
    2. @Target
    3. @Documented
    4. @Inherited

    1.2.1 @Retention

    作用:设置注解保留的截止时间

    public @interface Retention {
    	//保留策略,该变量是个枚举类型
        RetentionPolicy value();
    }
    
    public enum RetentionPolicy {
        //只保留在源代码中,编译后丢失
        SOURCE,
        //保留在源代码和class文件中,程序运行运行时丢失
        CLASS,
        //保留在源代码、class文件中,程序运行时jvm依然会保留该注解信息,可以通过反射回去该注解的信息
        RUNTIME
    }
    

    例:

    //该注解在程序运行时可以利用反射获取value值
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        String value() default  "";
    }
    

    1.2.2 @Target

    作用:指定该注解可以修饰哪些元素

    public @interface Target {
        //指定注解修饰的元素类型,该成员是个枚举数组,一次可以指定多个可修饰元素
        ElementType[] value();
    }
    
    public enum ElementType {
        /** 能修饰类、接口或枚举类型 */
        TYPE,
    
        /** 能修饰成员变量 */
        FIELD,
    
        /** 能修饰方法 */
        METHOD,
    
        /** 能修饰参数 */
        PARAMETER,
    
        /** 能修饰构造器 */
        CONSTRUCTOR,
    
        /** 能修饰局部变量 */
        LOCAL_VARIABLE,
    
        /** 能修饰注解 */
        ANNOTATION_TYPE,
    
        /** 能修饰包 */
        PACKAGE,
    
        /**
         * 可以用在 Type 的声明式前
         * @since 1.8
         */
        TYPE_PARAMETER,
    
        /**
         *可以用在所有使用 Type 的地方(如:泛型,类型转换等)
         * @since 1.8
         */
        TYPE_USE
    }
    

    示例1:单个ElementType

    @Target(ElementType.FIELD)
    public @interface MyAnnotation {
        String value() default  "";
    }
    

    示例2:多个ElementType

    @Target({ElementType.FIELD,ElementType.METHOD})
    public @interface MyAnnotation {
        String value() default  "";
    }
    

    1.2.3 @Documented

    使用了@Documented修饰自定义注解A,在使用javadoc命令生成API文档后,所有使用A注解修饰的程序元素,将会包含注解A的说明。

    1.2.4 @Inherited

    使用了@Inherited修饰注解可以被继承。

    三、注解的提取

    Java注解可以修饰类、类属性、类方法、类构造器等等,这些属性被注解修饰后不会自动的生效,需要我们把这些信息提取出来,加上处理代码,下面列举一些注解的一些常见的使用场景。

    1 修饰类

    1.1 定义注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @interface Entity{
        String name() default "";
        String description() default "这里可以对表模型进行一些描述";
    }
    

    1.2 使用注解

    @Entity(name = "user")
    class User{
    }
    

    1.3 提取注解信息

    public class AnnoTest {
        public static void main(String[] args) {
            //判断User类是否被@Entity注解修饰
            boolean present = User.class.isAnnotationPresent(Entity.class);
            if (present) {
                Entity annotation = User.class.getAnnotation(Entity.class);
                System.out.println("表名:"+annotation.name()+"
    "+"描述:"+annotation.description());
            }
        }
    }
    

    2 修饰类属性

    2.1 定义注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface Field{
        String name() default  "";
        boolean exist() default true;
    }
    

    2.2 使用注解

    class User{
        @Field(name = "username")
        private String username;
    }
    

    2.3 提取注解信息

    public class AnnoTest {
        public static void main(String[] args) throws NoSuchFieldException {
            Class<User> c = User.class;
            //根据属性名获取类属性
            java.lang.reflect.Field field = c.getDeclaredField("username");
            Field anno = field.getAnnotation(Field.class);
            if (anno != null) {
                System.out.println("表字段名:"+anno.name());
                System.out.println("是否存在该字段:"+anno.exist());
            }
        }
    }
    

    3 修饰类方法

    3.1 定义注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @interface Log{
        String user() default "";
        String operation() default "he";
    }
    

    3.2 使用注解

    class User{
        @Log(user = "张三",operation = "删除文章")
        public void deleteArticle() {
        }
    }
    

    3.3 提取注解信息

    public class AnnoTest {
        public static void main(String[] args) throws NoSuchFieldException {
            Class<User> c = User.class;
            //获取该类所有方法
            Method[] methods = c.getDeclaredMethods();
            if (methods.length>0) {
                for (Method m:methods) {
                    if (m.getName().equals("deleteArticle")) {
                        Log anno = m.getDeclaredAnnotation(Log.class);
                        if (anno != null) {
                            StringBuffer sb = new StringBuffer();
                            LocalDate now = LocalDate.now();
                            sb.append(anno.user()).append("于").append(now.toString()).append(",调用了方法“"+m.getName()+"”,进行了"+anno.operation()+"操作!");
                            System.out.println(sb.toString());
                        }
                    }
                }
            }
        }
    }
    

    四、注解的本质

    1 新建一个注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Log{
        @Deprecated
        String user() default "";
        String operation() default "喝茶";
    }
    

    2 编译注解

    打开dos命令框,在该文件目录下输入命令

    javac Log.java
    

    得到Log.class文件

    3 反编译log.class文件

    输入命令

    javap -verbose -c Log.class > Log.txt
    

    得到Log.txt字节码文件:
    @Log字节码信息说明
    总结

    1. 从反编译的信息里可以知道,注解就是一个继承java.lang.annotation.Annotation接口的接口。
    2. 注解的成员变量会被编译为同名的抽象方法,成员类型就是方法返回值类型,通过调用该注解对象的方法就能得到该方法对应的成员变量值。
    只有把命运掌握在自己手中,从今天起开始努力,即使暂时看不到希望,也要相信自己。因为比你牛几倍的人,依然在努力。
  • 相关阅读:
    【JavaP6大纲】Java基础篇:为什么jdk8以后HashMap会使用红黑树优化?
    【JavaP6大纲】Java基础篇:HashMap加载因子为什么是0.75?
    【JavaP6大纲】Zookeeper篇:选举机制
    就是要幸福(1)严于律人
    天真的童年
    闲言碎语话心得垃圾工作
    镜花水月
    就是要幸福(3)言行自由
    五年
    爸爸我给你捂捂手
  • 原文地址:https://www.cnblogs.com/freesky168/p/14358233.html
Copyright © 2011-2022 走看看