Java注解筆記

一、認(rèn)識注解

一直準(zhǔn)備寫一波關(guān)于學(xué)習(xí)Java SE方面的文章疏橄,但是從我大學(xué)第一次寫博客開始占拍,就比較零零散散,但是無論如何JavaSE還是基礎(chǔ)捎迫,還是需要認(rèn)真打好基礎(chǔ)晃酒。
廢話不多說,Java注解在開發(fā)中可是經(jīng)常遇到窄绒,Android的框架中都有注解的身影贝次,例如Retrofit、butterknife框架彰导,到處都是注解蛔翅,那么注解到底是神馬呢?如何使用注解位谋?我們怎樣自定義一個注解呢山析?別急,今天不僅有基礎(chǔ)知識掏父,博客最后還會包含一個小項目帶大家實(shí)踐實(shí)踐笋轨,試試手。
首先帶帶大家看看什么是注解:



其中@Override就是一個常見的注解赊淑,它代表的是重寫的意思翩腐,由于它是位于一個方法之上,所以他重寫了該方法膏燃,至于重寫父類什么并不是我們關(guān)心的重點(diǎn)茂卦,再來看一個:



@TargetApi這個注解是安卓開發(fā)中用來標(biāo)識該方法是哪個安卓版本的Api,其中小括號里的參數(shù)即代表哪個版本的Android系統(tǒng)API组哩,可以看到等龙,@TargetApi這個注解也是作用于方法上的处渣。類似于這種格式語法的注解有很多種。

好的蛛砰,那么學(xué)習(xí)注解有什么好處呢罐栈?
我列出以下三點(diǎn):
1.能夠讀懂別人寫的代碼,特別是框架相關(guān)的代碼泥畅!
2.讓編程更加簡潔荠诬,代碼更加清晰
3.讓別人高看一眼,裝逼位仁!

好了柑贞,直接上注解的定義:

注解:Java提供了一種原程序中的元素關(guān)聯(lián)任何信息和任何元數(shù)據(jù)的途徑和方法。
(Annotation(注解)是JDK1.5及以后版本引入的)

注解的分類有以下幾種:

按照運(yùn)行機(jī)制來分(作用時間):
1.源碼注解 //注解只在源碼中存在聂抢,編譯成.class文件就不存在了
2.編譯時的注解钧嘶、 //注解在源碼和.class文件中都存在,只在編譯時起作用琳疏。@Override
3.運(yùn)行時的注解 //在運(yùn)行階段還起作用有决,甚至?xí)绊戇\(yùn)行邏輯的注解。

按照來源來分
1.來自JDK的注解
2.來自第三方的注解‘
3.我們自己定義的注解

二空盼、創(chuàng)建注解

現(xiàn)在我們來看看一個自定義注解的語法要求:

// 我們自定義一個注解的基本格式
@Target({ElementType.METHOD,ElementType.TYPE})  //可以指定被注解的類型
@Retention(RetentionPolicy.RUNTIME)             //注解類型:運(yùn)行時书幕、編譯時
@Inherited   //是否允許子類繼承注解 接口不起作用 需要是類繼承
@Documented
//如果注解只有一個成員,則這個成員名字必須取名為value()揽趾,方便在使用時的默認(rèn)參數(shù)
public @interface Description {

    //成員必須無參 無異常方式聲明
    String desc();

    //成員類型是受限的按咒,合法類型:String Class Annotation Enumeration
    String author();

    int age() default 18;
}

可以看到這就是一個自定義注解的大體結(jié)構(gòu)。
@interface 自定義注解的關(guān)鍵字
下面四個是元注解:
@Target 指定作用域 但骨,參數(shù)可以為構(gòu)造方法励七、字段、局部變量奔缠、方法掠抬、包、參數(shù)校哎、類或接口Type
@Retention 指定注解類型
@Inherited 是否允許繼承
@Documented 生成doc說明文檔
然后自定義注解 public @interface 注解名 {
定義各種方法
}
大括號里面成員必須符合:
成員類型是受限制的两波,只能是String、Class闷哆、Annotation腰奋、Enumeration類型
成員必須無參數(shù)且無異常方式聲明。
可以指定default為成員指定一個默認(rèn)值
注意:如果注解只有一個成員的話抱怔,則成員名必須為value(),方便在使用時忽略成員名和賦值號
注解可以沒有成員劣坊,則這個注解稱為標(biāo)識注解!

好了屈留,這樣一個叫Description的注解被我們create出來了局冰,而且是可以在方法上和類上都能使用這個注解测蘑,我們把它聲明為是一個運(yùn)行時注解,不允許子類繼承康二,可以生成doc文檔碳胳。
注意:這個注解里面含有三個方法。

三沫勿、使用注解

那么我們怎么使用這個注解呢挨约?
使用注解的語法格式:
@<注解名>(<成員名1>=<成員值1>,<成員名2>=<成員值2>,....)
拿上面注解的舉個例子:

@Description(desc="I am eyeColor",author="Mooc boy",age=18)
public String eyeColor() {
return "red";
}

可以看到這個使用自定義注解是作用于方法上产雹。
Ok诫惭,create注解和使用自定義注解的都已經(jīng)完成,還是很簡單的洽故。但是我們這個注解還沒有實(shí)際的作用,我們可以利用反射來完成注解的邏輯功能盗誊。

解析注解

為了讓我們自定義的注解能有“實(shí)際意義”时甚,我們可以通過解析自定義注解,來讓我們創(chuàng)建的代碼能夠控制邏輯哈踱。
通過反射獲取類,函數(shù),或成員上的運(yùn)行時注解信息,從而實(shí)現(xiàn)動態(tài)控制程序運(yùn)行時的邏輯
為了便于理解荒适,這里我舉個簡單完整的例子:
1.Create一個名叫Guikai注解

import java.lang.annotation.*;

@Target({ElementType.METHOD,ElementType.TYPE})   //元注解 注解作用類型
@Retention(RetentionPolicy.RUNTIME)              //指定注解的類型  運(yùn)行時
@Documented
@Inherited
public @interface Guikai {
    String Name();
    String Num();
   String Class();
}

2.新建一個類,使用這個注解

@Guikai(Name = "這是一個類上的注解",Num = "2014911006",Class = "計本一班")
public class Person {
    @Guikai(Name = "這是一個方法上的注解",Num = "2014911006",Class = "計本一班")
    public String name() {
        return null;
    }
    public int age() {
        return 0;
    }
}

可以看到我們在方法和類上都加上我們自定義的注解

3.解析注解代碼實(shí)例

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
 * 解析注解:
 * 通過反射獲取類,函數(shù),或成員上的運(yùn)行時注解信息,從而實(shí)現(xiàn)動態(tài)控制程序運(yùn)行時的邏輯
 */
 
public class Sample {
    public static void main(String[] args) {
        try {
            //獲取類對象开镣,判斷類上面是否有注解
            Class c = Class.forName("annotation.Person");
            boolean isExist = c.isAnnotationPresent(Guikai.class);
            if (isExist) {
                Guikai d = (Guikai) c.getAnnotation(Guikai.class);
                System.out.println(d.Name() + d.Num() + d.Class());
            }

            //接下來獲得方法上的注解
            Method[] ms = c.getMethods();
            for (Method item : ms) {
                //反射獲取方法列表刀诬,然后遍歷每個方法,判斷是否有注解
                boolean isMExit = item.isAnnotationPresent(Guikai.class);
                if (isMExit) {
                    Guikai g = item.getAnnotation(Guikai.class);
                    System.out.println(g.Name() + g.Num() + g.Class());
                }
            }
            //第二種方法
            for (Method m : ms) {
                Annotation[] as = m.getAnnotations();
                for (Annotation a : as) {
                    if (a instanceof Guikai) {
                        Guikai d = (Guikai) a;
                        System.out.println(d.Name() + d.Num() + d.Class());
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果:

這是一個類上的注解2014911006計本一班
這是一個方法上的注解2014911006計本一班
這是一個方法上的注解2014911006計本一班

這樣我們就通過反射邪财,獲取到類上面的注解陕壹,然后打印出guikai注解上的參數(shù)!
接下來树埠,我將附上一個生成sql語句的例子糠馆,讓大家感受一下注解的強(qiáng)大!

四怎憋、Demo實(shí)戰(zhàn)

注解的基礎(chǔ)知識我們講的差不多了又碌,接下來來一個注解實(shí)戰(zhàn):
項目取自一個公司的持久層框架,用來代替Hibernate的解決方法绊袋,核心代碼就是通過注解來實(shí)現(xiàn)毕匀,
當(dāng)然一個商業(yè)級的項目過于復(fù)雜,我們這里只是抽離出核心代碼出來癌别。
需求:
1.有一張用戶表皂岔,字段包括用戶ID、用戶名展姐、昵稱凤薛、年齡姓建,性別,所在城市缤苫,郵箱速兔,手機(jī)號。
2.方便對每一個字段或字段的組合條件進(jìn)行檢索活玲,并打印出SQL語句涣狗。
3.使用的方式需要足夠簡單!

首先我們需要一個Bean類(對應(yīng)數(shù)據(jù)表字段)
Filter.java文件:

package com.example.dao;

@Table("user")
public class Filter {
    @Column("id")
    private int id;
    @Column("user_name")
    private String userName;
    @Column("nick_name")
    private String nickName;
    @Column("age")
    private int age;
    @Column("city")
    private String city;
    @Column("email")
    private String email;
    @Column("mobile")
    private String mobile;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getNickName() {
        return nickName;
    }
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getMobile() {
        return mobile;
    }
    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
}

在Bean類的類名和方法上舒憾,有兩個我們自定義的注解镀钓,分別為Column、Table镀迂,其只有一個成員

Column.java

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

@Target({ElementType.FIEID})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String value();
}

Table.java

package com.example.dao;

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

@Target({ElementType.FIEID})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value();
}

Test.java測試類:

package com.example.dao;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) {
        Filter f1 = new Filter();
        f1.setId(10);   //查詢?yōu)?0的用戶

        Filter f2 = new Filter();
        f2.setUserName("lucy"); //模糊查詢用戶名為lucy的用戶

        Filter f3 = new Filter();
        f3.setEmail("guikai@qq.com,zh@163.com,77777@qq.com");//查詢郵箱為其中任意一個的用戶

        String sql1 = query(f1);
        String sql2 = query(f2);
        String sql3 = query(f3);

        System.out.println(sql1);
        System.out.println(sql2);
        System.out.println(sql3);

    }

    private static String query(Filter f) {
        StringBuffer sb = new StringBuffer();
        //1.獲取到class
        Class c = f.getClass();
        //2.獲取到table的名字
        boolean exists = c.isAnnotationPresent(Table.class);
        if (!exists) {
            return null;
        }
        Table table = (Table) c.getAnnotation(Table.class);
        String tableName = table.value();
        sb.append("Select * from ").append(tableName).append(" where 1=1");
        //3.遍歷所有的字段
        Field[] fArray = c.getDeclaredFields();
        for (Field field:fArray) {
            //4.處理每個字段對應(yīng)的sql
            //4.1 拿到字段的名字
            boolean fExists = field.isAnnotationPresent(Column.class);
            if (!fExists) {
                continue;
            }
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();
            //4.2 拿到字段的值
            String filedName = field.getName();
            String getMethodName = "get" + filedName.substring(0, 1).toUpperCase()
                    + filedName.substring(1);
            Object fieldValue = null;
            try {
                Method getMethod = c.getMethod(getMethodName);
                fieldValue = (Object) getMethod.invoke(f);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //4.3 拼裝sql
            if (fieldValue==null ||
                    (fieldValue instanceof  Integer && (Integer)fieldValue==0)) {
                continue;
            }
            sb.append(" and ").append(filedName);

            if (fieldValue instanceof String) {
                if (((String) fieldValue).contains(",")){
                    String[]  values = ((String) fieldValue).split(",");
                    sb.append(" in(");
                    for (String v:values) {
                        sb.append("'").append(v).append("'").append(",");
                    }
                    sb.deleteCharAt(sb.length()-1);
                    sb.append(")");
                } else {
                    sb.append("=").append("'").append(fieldValue).append("'");
                }
            } else if (fieldValue instanceof Integer) {
                sb.append("=").append(fieldValue);
            }
        }
        return sb.toString();
    }
}

可以看到丁溅,整個項目比較的簡單,通過調(diào)用query(Filter f)方法探遵,來實(shí)現(xiàn)打印SQL語句窟赏,下面我們詳細(xì)看看這個方法做了什么:
毋庸置疑,也是通過反射Filte類來實(shí)現(xiàn)邏輯的箱季,根據(jù)反射得到類里面的信息涯穷,包括類名上的注解和方法上的注解,然后獲取注解的值藏雏,最后經(jīng)過拼接字符串來實(shí)現(xiàn)SQL語句返回拷况!

最后附上源碼:
https://github.com/Gui-kai/BaseJava

覺得好,請點(diǎn)個贊掘殴!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赚瘦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子奏寨,更是在濱河造成了極大的恐慌蚤告,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件服爷,死亡現(xiàn)場離奇詭異杜恰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)仍源,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門心褐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笼踩,你說我怎么就攤上這事逗爹。” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵掘而,是天一觀的道長挟冠。 經(jīng)常有香客問我,道長袍睡,這世上最難降的妖魔是什么知染? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮斑胜,結(jié)果婚禮上控淡,老公的妹妹穿的比我還像新娘。我一直安慰自己止潘,他們只是感情好掺炭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凭戴,像睡著了一般涧狮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上么夫,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天者冤,我揣著相機(jī)與錄音,去河邊找鬼魏割。 笑死譬嚣,一個胖子當(dāng)著我的面吹牛钢颂,可吹牛的內(nèi)容都是我干的钞它。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼殊鞭,長吁一口氣:“原來是場噩夢啊……” “哼遭垛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起操灿,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤锯仪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后趾盐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庶喜,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年救鲤,在試婚紗的時候發(fā)現(xiàn)自己被綠了久窟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡本缠,死狀恐怖斥扛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丹锹,我是刑警寧澤稀颁,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布芬失,位于F島的核電站,受9級特大地震影響匾灶,放射性物質(zhì)發(fā)生泄漏棱烂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一粘昨、第九天 我趴在偏房一處隱蔽的房頂上張望垢啼。 院中可真熱鬧,春花似錦张肾、人聲如沸芭析。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馁启。三九已至,卻和暖如春芍秆,著一層夾襖步出監(jiān)牢的瞬間惯疙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工妖啥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留霉颠,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓荆虱,卻偏偏與公主長得像蒿偎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子怀读,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348