TVI-Android技術篇之註解Annotation

開幕:初見

首先看一下家喻戶曉的@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
 * 郵箱:[email protected]
 * 說明:註解類
 */

@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
 * 郵箱:[email protected]
 * 說明:註解測試端
 */
@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
 * 郵箱:[email protected]
 * 說明:註解測試端
 */
@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.
這時你已經可以通過註解來獲取信息了

第四幕:共鳴

人類創造了刀,有些人用它雕精美的藝術品,有人依靠它成爲江湖浪客,有人以它護生,有人用它殺生。
成敗善惡並非工具的榮辱,也非是鍛造它的人,一切只取決於握刀人的本性與技藝。

下面通過兩個簡單示例實戰一下

示例一:聲明:下面的案例借鑑並修改於:http://www.javashuo.com/article/p-sroqmnzv-ko.html
package top.toly.註解;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * 作者:張風捷特烈
 * 時間:2018/5/22:10:16
 * 郵箱:[email protected]
 * 說明:註解類
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface ADebug {

}
package top.toly.註解;

import java.lang.reflect.Method;

/**
 * 作者:張風捷特烈
 * 時間:2018/5/22:10:17
 * 郵箱:[email protected]
 * 說明:測試工具類
 */
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
 * 郵箱:[email protected]
 * 說明:待測試類
 */
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
 * 郵箱:[email protected]
 * 說明:運行端
 */
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
 * 郵箱:[email protected]
 * 說明:數據庫列(字段)註解
 */

@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
 * 郵箱:[email protected]
 * 說明:數據庫表註解
 */

@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
 * 郵箱:[email protected]
 * 說明:
 */
@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
 * 郵箱:[email protected]
 * 說明:使用註解輔助得到查詢語句
 */
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
 * 郵箱:[email protected]
 * 說明:測試端
 */
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驗證一下:
9414344-8cbeada52c365afb.png
20180523152021788.png

終幕


後記、

1.聲明:

[1]本文由張風捷特烈原創,轉載請註明
[2]歡迎廣大編程愛好者共同交流
[3]個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
[4]你的喜歡與支持將是我最大的動力

2.連接傳送門:

更多安卓技術歡迎訪問:安卓技術棧
我的github地址:歡迎star
簡書首發,騰訊雲+社區同步更新
張風捷特烈個人網站,編程筆記請訪問:http://www.toly1994.com

3.聯繫我

QQ:1981462002
郵箱:[email protected]
微信:zdl1994328

4.歡迎關注我的微信公衆號,最新精彩文章,及時送達:
9414344-c474349cd3bd4b82.jpg
公衆號.jpg