美文网首页Android开发Java 杂谈Android技术栈
TVI-Android技术篇之注解Annotation

TVI-Android技术篇之注解Annotation

作者: e4e52c116681 | 来源:发表于2018-09-10 17:06 被阅读23次

    开幕:初见

    首先看一下家喻户晓的@Override注解:添加此注解,如果是非覆写的方法,就会报错
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Override {
    }
    
    再先看一下@Deprecated注解:添加此注解,如果是过时的方法,就会画线提示
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
    public @interface Deprecated {
        String since() default "";
        boolean forRemoval() default false;
    }
    
    我们这个群体应该很擅长归纳事物的共性,然后总结出一丝规律
    可以看到的是:
    public @interface 注解名{
        
    }
    因此,可依照这样自己写一个注解类:
    public @interface APerson {
    
    }
    然后新建一个Person类看看能不能用:
    @APerson
    public class Person {
    
    }
    
    编译器没报错,看了可以,于是你的第一个没用的注解就由此诞生,开幕止。

    第一幕:相识:

    原标签
    1:@Retention(注解存活期):接受一个RetentionPolicy类型的枚举常量
    //源码的RetentionPolicy枚举
    public enum RetentionPolicy {
        /**
         * Annotations are to be discarded by the compiler.
         * 注解将会被编译器删除
         */
        SOURCE,
    
        /**
         * Annotations are to be recorded in the class file by the compiler
         * but need not be retained by the VM at run time.  This is the default
         * behavior.
         * 编译器会将注解保留在字节码文件中,但VM不会再运行期间保留它。这是默认行为
         */
        CLASS,
    
        /**
         * Annotations are to be recorded in the class file by the compiler and
         * retained by the VM at run time, so they may be read reflectively.
         * 编译器会将注解保留在字节码文件中,VM也会在运行期间保留它。(所以他们可以通过反射性被读取)
         * @see java.lang.reflect.AnnotatedElement
         */
        RUNTIME
    }
    
    
    创建一个运行期的注解
    @Retention(RetentionPolicy.RUNTIME)
    public @interface APerson {
    
    }
    
    2:@Target(目标):接受一个ElementType类型的枚举常量
    //源码枚举类:ElementType
    public enum ElementType {
        /** Class, interface (including annotation type), or enum declaration */
        //声明类,接口(包括注解类型),或者枚举
        TYPE,   
        /** Field declaration (includes enum constants) */
        //声明字段(包括枚举常量)
        FIELD,  
    
        /** Method declaration */
        //声明方法
        METHOD,
    
        /** Formal parameter declaration */
        //声明方法参数
        PARAMETER,
    
        /** Constructor declaration */
        //声明构造函数
        CONSTRUCTOR,
    
        /** Local variable declaration */
        //声明局部变量
        LOCAL_VARIABLE,
    
        /** Annotation type declaration */
        //声明注解
        ANNOTATION_TYPE,
    
        /** Package declaration */
        //声明包
        PACKAGE,
    
        /**
         * Type parameter declaration
         *  声明参数类型
         * @since 1.8
         */
        TYPE_PARAMETER,
    
        /**
         * Use of a type
         *  
         * @since 1.8
         */
        TYPE_USE,
    
        /**
         * Module declaration.
         *  声明模块
         * @since 9
         */
        MODULE
    }
    
    3:@Inherited(继承):子类继承父类的注解
    4:@Repeatable(可重复)
    5:@Documented:能够将注解中的元素包含到 Javadoc。
    已经同注解进行了基本的对话(了解),第二幕止。

    第三幕:交涉

    改善一下我们的注解
    package top.toly.注解;
    
    import java.lang.annotation.*;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/22:8:20
     * 邮箱:1981462002@qq.com
     * 说明:注解类
     */
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface APerson {
        public String name() default "捷特";
        public int age() default 24;
        
    }
    
    使用反射获取APerson对象,再得到其方法
    package top.toly.注解;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/22:8:21
     * 邮箱:1981462002@qq.com
     * 说明:注解测试端
     */
    @APerson(name = "捷特", age = 24)
    public class Person {
        APerson aPerson = getClass().getAnnotation(APerson.class);
    
        public void say() {
            String name = aPerson.name();
            int age = aPerson.age();
            System.out.println("my name is "+name+",and I am "+age+"years old");
        }
    
    
        public static void main(String[] args) {
            Person person = new Person();
            person.say();
        }
    }
    
    输出结果
    my name is 捷特,and I am 24 years old.
    
    现在测试一下@Inherited(继承):
    package top.toly.注解;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/22:8:21
     * 邮箱:1981462002@qq.com
     * 说明:注解测试端
     */
    @APerson(name = "捷特", age = 24)
    public class Person {
        APerson aPerson = getClass().getAnnotation(APerson.class);
    
        public void say() {
            String name = aPerson.name();
            int age = aPerson.age();
            System.out.println("my name is "+name+",and I am "+age+" years old.");
        }
    
    
        public static void main(String[] args) {
            Student student = new Student();
            student.say();
        }
    }
    
    class Student extends Person {
    
    }
    
    
    运行:报错
    Exception in thread "main" java.lang.NullPointerException
        at top.toly.注解.Person.say(Person.java:14)
        at top.toly.注解.Person.main(Person.java:22)
    
    添加@Inherited注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Inherited
    public @interface APerson {
        public String name() default "捷特";
        public int age() default 24;
    }
    
    
    运行:
    //
    my name is 捷特,and I am 24 years old.
    
    这时你已经可以通过注解来获取信息了

    第四幕:共鸣

    人类创造了刀,有些人用它雕精美的艺术品,有人依靠它成为江湖浪客,有人以它护生,有人用它杀生。
    成败善恶并非工具的荣辱,也非是锻造它的人,一切只取决于握刀人的本性与技艺。
    

    下面通过两个简单示例实战一下

    示例一:声明:下面的案例借鉴并修改于:https://blog.csdn.net/briblue/article/details/73824058
    package top.toly.注解;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/22:10:16
     * 邮箱:1981462002@qq.com
     * 说明:注解类
     */
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ADebug {
    
    }
    
    
    package top.toly.注解;
    
    import java.lang.reflect.Method;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/22:10:17
     * 邮箱:1981462002@qq.com
     * 说明:测试工具类
     */
    public class Debug {
    
        public static void debug(String clazz_name) {
    
            Class<?> clazz = null;//获取测试类字节码文件
            Object testobj = null;//通过字节码获取实例
            try {
                clazz = Class.forName(clazz_name);
                testobj = clazz.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            Method[] method = clazz.getDeclaredMethods();//通过字节码获取所有方法
            //用来记录测试产生的 log 信息
            StringBuilder log = new StringBuilder();
            // 记录异常的次数
            int errornum = 0;
    
            for (Method m : method) {
                // 只有被 @ADebug 标注过的方法才进行测试
                if (m.isAnnotationPresent(ADebug.class)) {
                    try {
                        m.setAccessible(true);
                        m.invoke(testobj, null);//执行方法
                    } catch (Exception e) {
                        errornum++;
                        log.append("错误"+errornum+":"+m.getName()+"() has error:");
                        log.append("\n\r  caused by ");
                        //记录测试过程中,发生的异常的名称
                        log.append(e.getCause().getClass().getSimpleName());
                        log.append("\n\r");
                        //记录测试过程中,发生的异常的具体信息
                        log.append(e.getCause().getMessage());
                        log.append("\n\r");
                    }
                }
            }
    
            log.append(clazz.getSimpleName());
            log.append(" has  ");
            log.append(errornum);
            log.append(" error.");
    
            // 生成测试报告
            System.out.println(log.toString());
    
        }
    }
    
    
    
    package top.toly.注解;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/22:10:18
     * 邮箱:1981462002@qq.com
     * 说明:待测试类
     */
    public class BugTest {
        @ADebug
        public void say(){
            System.out.println(Integer.parseInt("a"));
        }
        @ADebug
        public void jia(){
            System.out.println("1+1="+1+1);
        }
        @ADebug
        public void jian(){
            System.out.println("1-1="+(1-1));
        }
        @ADebug
        public void cheng(){
            System.out.println("3 x 5="+ 3*5);
        }
        @ADebug
        public void chu(){
            System.out.println("6 / 0="+ 6 / 0);
        }
    
        public void resay(){
            say();
        }
    
    }
    
    
    package top.toly.注解;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/22:10:28
     * 邮箱:1981462002@qq.com
     * 说明:运行端
     */
    public class Client {
        public static void main(String[] args) {
            Debug.debug("top.toly.注解.BugTest");
        }
    }
    
    

    输出:

    1-1=0
    3 x 5=15
    1+1=11
    错误1:say() has error:
      caused by NumberFormatException
    For input string: "a"
    错误2:chu() has error:
      caused by ArithmeticException
    / by zero
    BugTest has  2 error.
    
    可以看到未加注解的方法,即使错了也不会检查到。
    注解更像提示你一下到这要不要做些什么事,具体逻辑还需要具体的类来实现。
    唯一的优势在于你知道了程序已经运行到注解处,还有你可以获取到注解中的字段值。
    
    示例二:根据一个bean对象,来输处MySQL的查询语句
    1.数据库列(字段)注解
    package top.toly.注解.test;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/22:23:27
     * 邮箱:1981462002@qq.com
     * 说明:数据库列(字段)注解
     */
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Column {
        String value();
    }
    
    
    2.数据库表注解
    package top.toly.注解.test;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/22:23:27
     * 邮箱:1981462002@qq.com
     * 说明:数据库表注解
     */
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Table {
        String value();
    }
    
    
    3.bean对象Sworld:曾经创建过一个sword的数据库,正好拿来用。(对MySQL不熟悉的可以看我的MySQL篇)
    package top.toly.注解.test.bean;
    
    import top.toly.注解.test.Column;
    import top.toly.注解.test.Table;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/23:14:47
     * 邮箱:1981462002@qq.com
     * 说明:
     */
    @Table("sword")
    public class Sword {
        @Column("id")
        private int id;
        @Column("name")
        private String name;
        @Column("atk")
        private int atk;
        @Column("hit")
        private int hit;
        @Column("crit")
        private int crit;
        @Column("attr_id")
        private int attr_id;
        @Column("type_id")
        private int type_id;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAtk() {
            return atk;
        }
    
        public void setAtk(int atk) {
            this.atk = atk;
        }
    
        public int getHit() {
            return hit;
        }
    
        public void setHit(int hit) {
            this.hit = hit;
        }
    
        public int getCrit() {
            return crit;
        }
    
        public void setCrit(int crit) {
            this.crit = crit;
        }
    
        public int getAttr_id() {
            return attr_id;
        }
    
        public void setAttr_id(int attr_id) {
            this.attr_id = attr_id;
        }
    
        public int getType_id() {
            return type_id;
        }
    
        public void setType_id(int type_id) {
            this.type_id = type_id;
        }
    }
    
    
    4.核心类:QueryUtil:使用注解辅助得到查询语句
    package top.toly.注解.test;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/23:14:53
     * 邮箱:1981462002@qq.com
     * 说明:使用注解辅助得到查询语句
     */
    public class QueryUtil {
        public static String query(Object o) {
            StringBuffer sb = new StringBuffer();
    
            //1.获取class
            Class<?> aClass = o.getClass();
            //2.获取表名
            boolean exist = aClass.isAnnotationPresent(Table.class);
            if (exist) {
                Table table = aClass.getAnnotation(Table.class);
                String tableName = table.value();
                sb.append("SELECT * FROM ").append(tableName).append(" WHERE 1=1");
                //3.遍历字段
                Field[] fields = aClass.getDeclaredFields();
                for (Field field : fields) {
                    //4.处理字段对应的sql
                    boolean b = field.isAnnotationPresent(Column.class);
                    if (!b) {
                        continue;
                    }
                    Column column = field.getAnnotation(Column.class);
                    String columnName = column.value();
                    String fieldName = field.getName();
                    String getMethodName = "get" + fieldName.substring(0, 1)
                            .toUpperCase() + fieldName.substring(1);
                    Object fieldValue = null;
                    try {
                        Method getMethod = aClass.getMethod(getMethodName);
                        fieldValue = getMethod.invoke(o);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    //拼装sql
                    if (fieldValue == null || (fieldValue instanceof Integer && (Integer) fieldValue == 0)) {
                        continue;
                    }
                    sb.append(" and ").append(fieldName);
                    if (fieldValue instanceof String) {
                        String value = (String) fieldValue;
                        if (value.contains(",")) {
                            String[] strings = value.split(",");
                            sb.append(" in(");
                            for (String string : strings) {
                                sb.append("'").append(string).append("'").append(",");
                            }
                            sb.deleteCharAt(sb.length() - 1);
                            sb.append(")");
                        }else {
                            sb.append("=").append("'" + fieldValue + "'");
                        }
                    }else {
                        sb.append("=").append(fieldValue);
                    }
                }
            }
            return sb.toString();
        }
    }
    
    
    5.测试端
    package top.toly.注解.test;
    
    import top.toly.注解.test.bean.Sword;
    
    /**
     * 作者:张风捷特烈
     * 时间:2018/5/23:14:54
     * 邮箱:1981462002@qq.com
     * 说明:测试端
     */
    public class Client {
        public static void main(String[] args) {
            Sword sabar = new Sword();
            sabar.setName("炽燃");
            sabar.setAtk(2000);
    
            System.out.println(QueryUtil.query(sabar));
    
        }
    }
    
    
    6.打印结果
    SELECT * FROM sword WHERE 1=1 and name='炽燃' and atk=2000
    
    用MySQL验证一下:
    20180523152021788.png

    终幕


    后记、

    1.声明:

    [1]本文由张风捷特烈原创,转载请注明
    [2]欢迎广大编程爱好者共同交流
    [3]个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
    [4]你的喜欢与支持将是我最大的动力

    2.连接传送门:

    更多安卓技术欢迎访问:安卓技术栈
    我的github地址:欢迎star
    简书首发,腾讯云+社区同步更新
    张风捷特烈个人网站,编程笔记请访问:http://www.toly1994.com

    3.联系我

    QQ:1981462002
    邮箱:1981462002@qq.com
    微信:zdl1994328

    4.欢迎关注我的微信公众号,最新精彩文章,及时送达:
    公众号.jpg

    相关文章

      网友评论

        本文标题:TVI-Android技术篇之注解Annotation

        本文链接:https://www.haomeiwen.com/subject/ohasgftx.html