一、 前言
我們大家都知道Java代碼中使用注釋是為了向以后閱讀這份代碼的人解釋說(shuō)明一些事情恢氯,注解是注釋的升級(jí)版带斑,它可以向編譯器、虛擬機(jī)等解釋說(shuō)明一些事情勋拟。比如我們非常熟悉的@Override就是一種元注解勋磕,它的作用是告訴編譯器它所注解的方法是重寫(xiě)父類(lèi)的方法,這樣編譯器就會(huì)去檢查父類(lèi)是否存在這個(gè)方法敢靡,以及這個(gè)方法的簽名與父類(lèi)是否相同挂滓。
我們?yōu)槭裁磳W(xué)習(xí)注解?學(xué)習(xí)注解有什么好處啸胧?學(xué)完能做什么赶站?
(1)、能夠讀懂別人的代碼纺念,特別是框架相關(guān)的代碼贝椿。
(2)、讓編程更加簡(jiǎn)潔陷谱,代碼更加清晰烙博。
(3)、讓別人高看一眼。
二习勤、 Java中的常見(jiàn)注解
1、JDK自帶注解@Override焙格、@Deprecated图毕、@Suppvisewarnings
(1)、@Override
定義
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
從這個(gè)注解類(lèi)的定義我們可以看到眷唉,這個(gè)注解可以被用來(lái)修飾方法予颤,并且它只在源碼時(shí)有效,在編譯后的class文件中便不存在冬阳。
這個(gè)注解的主要作用是告訴編譯器被修飾的方法是重寫(xiě)的父類(lèi)中的相同簽名的方法蛤虐,編譯器會(huì)對(duì)此做出檢查,若發(fā)現(xiàn)父類(lèi)中不存在這個(gè)方法或是存在的方法簽名不同肝陪,則會(huì)報(bào)錯(cuò)驳庭。
(2)、@Deprecated
定義
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
從它的定義我們可以知道氯窍,它會(huì)被文檔化饲常,能夠保留到運(yùn)行時(shí),能夠修飾構(gòu)造方法狼讨、屬性贝淤、局部變量、方法政供、包播聪、參數(shù)、類(lèi)或接口布隔。
這個(gè)注解的作用是告訴編譯器被修飾的程序元素已被“廢棄”离陶,不再建議用戶使用。
(3)衅檀、@Suppvisewarnings
定義
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
它能夠修飾的程序元素包括類(lèi)或接口枕磁、屬性、方法术吝、參數(shù)计济、構(gòu)造器、局部變量排苍,只能存活在源碼時(shí)沦寂,取值為String[]。它的作用是告訴編譯器忽略指定的警告信息淘衙,它可以取的值如下所示:
deprecation:忽略使用了廢棄的類(lèi)或方法時(shí)的警告传藏;
unchecked:執(zhí)行了未檢查的轉(zhuǎn)換
fallthrough:swich語(yǔ)句款中case忘加break從而直接“落入”下一個(gè)case;
path:類(lèi)路徑或原文件路徑等不存在;
all:以上所有情況產(chǎn)生的警告均忽略毯侦。
(4)哭靖、實(shí)際使用介紹
自定義一個(gè)接口
/**
* 動(dòng)物類(lèi)接口
*/
public interface Animal {
public void eat();
public void sleep();
public void speak();
public void sing();
}
定義Dog類(lèi)實(shí)現(xiàn)Animal接口
/**
* 狗的實(shí)現(xiàn)類(lèi)
*
* @author YUBIN
*/
public class Dog implements Animal {
@Override
public void eat() {
}
@Override
public void sleep() {
}
@Override
public void speak() {
}
@Override
public void sing() {
}
}
此時(shí)你們?cè)贒og類(lèi)上的方法中看到@Override注解,表示該方法是重寫(xiě)或?qū)崿F(xiàn)父類(lèi)或接口的方法侈离,如果此時(shí)你在Dog類(lèi)中再書(shū)寫(xiě)一個(gè)接口中不存在的方法時(shí)试幽,然后在方法上使用@Override注解,則編譯檢查會(huì)報(bào)錯(cuò)卦碾,如下圖
我們仔細(xì)分析Animal這個(gè)接口铺坞,可以發(fā)現(xiàn)sing這個(gè)唱歌方法并不適用于所有的動(dòng)物,我們應(yīng)該把它去除洲胖,但是可能已經(jīng)有人使用了我們的這個(gè)接口济榨,這時(shí)我們應(yīng)該使用@Deprecated注解來(lái)告知?jiǎng)e人該方法已經(jīng)過(guò)時(shí)了。
效果:
但是這樣會(huì)導(dǎo)致代碼中出現(xiàn)警告圖標(biāo)绿映,對(duì)于有要求的公司來(lái)說(shuō)擒滑,是需要去除警告的,可以使用@SuppressWarnings("deprecation")來(lái)去除這個(gè)警告,這樣一來(lái)這個(gè)警告就消失了
三叉弦、 注解分類(lèi)
1橘忱、按照運(yùn)行機(jī)制分
(1)、源碼注解:注解只在源碼中存在卸奉,編譯成.class文件就不存在了栈幸。
(2)谨胞、編譯時(shí)注解:注解在源碼和.class文件中都存在末贾。
(3)筐钟、在運(yùn)行階段還起作用,甚至?xí)绊戇\(yùn)行邏輯的注解疹鳄。
元注解:注解的注解叫做元注解
四拧略、 自定義注解
1、自定義注解分語(yǔ)法要求
(1)瘪弓、使用@interface關(guān)鍵字定義注解
(2)垫蛆、成員以無(wú)參數(shù)無(wú)異常方式聲明
(3)、可以使用default為成員指定默認(rèn)值
(4)腺怯、成員類(lèi)型是受限的袱饭,合法的類(lèi)型包括基本數(shù)據(jù)類(lèi)型及String、Class呛占、Annotation虑乖、Enumeration
(5)、如果注解只有一個(gè)成員晾虑,則成員建議取名為value()疹味,在使用的時(shí)可以忽略成員名和賦值號(hào)(=)
2仅叫、注解的作用域
使用@Target來(lái)指明注解的作用域,表示該注解可以使用在什么地方糙捺,如下圖
CONSTRUCATOR:構(gòu)造方法聲明
FIELD:字段聲明
LOCAL_VARIABLE:局部變量聲明
METHOD:方法聲明
PACKAGE:包聲明
PARAMETER:參數(shù)聲明
TYPE:類(lèi)或接口
3诫咱、注解的生命周期
使用@Retention來(lái)指明定義的注解的生命周期
SOURCE:只在源碼中顯示,編譯時(shí)會(huì)丟棄
CLASS:編譯時(shí)會(huì)記錄到class中洪灯,運(yùn)行時(shí)忽略
RUNTIME:運(yùn)行時(shí)存在坎缭,可以通過(guò)反射讀取
4、@Inherited 允許子類(lèi)繼承
使用該注解婴渡,可以讓定義在父類(lèi)的注解被子類(lèi)所獲取,注意對(duì)接口無(wú)效凯亮。
5边臼、@Documented
生成javadoc時(shí)會(huì)包含注解
五、 解析注解
通過(guò)反射獲取類(lèi)假消、函數(shù)或成員上的運(yùn)行時(shí)注解信息柠并,從而實(shí)現(xiàn)動(dòng)態(tài)控制程序運(yùn)行的邏輯。在java.lang.redlect反射包中提供了一個(gè)接口AnnotatedElement富拗,該接口提供了獲取注解信息的幾個(gè)方法臼予,Class、Constructor啃沪、Field粘拾、Method、Package等都實(shí)現(xiàn)了該接口创千。
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass); // 判斷指定元素上是否存在指定注解
<T extends Annotation> T getAnnotation(Class<T> annotationClass); // 獲取指定元素的指定注解
Annotation[] getAnnotations(); // 獲取指定元素上存在的注釋
Annotation[] getDeclaredAnnotations(); // 獲取指定元素上存在的直接注釋缰雇,非父類(lèi)的
運(yùn)行時(shí)注解解析案例:
public class Test {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
Class<BigDog> bigDogClass = BigDog.class;
// 判斷類(lèi)上是否存在MyAnnotation注解
boolean isExist = bigDogClass.isAnnotationPresent(MyAnnotation.class);
if (isExist) {
MyAnnotation annotation = bigDogClass.getAnnotation(MyAnnotation.class);
System.out.println(annotation.desc());
}
}
}
六、 注解應(yīng)用實(shí)戰(zhàn)
下面我們以一個(gè)通過(guò)注解來(lái)實(shí)現(xiàn)生成sql為例追驴,來(lái)理解自定義注解的使用
1械哟、定義@Table注解
@Target(ElementType.TYPE) // 注解的作用范圍 作用在類(lèi)或接口上
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期 運(yùn)行時(shí)
public @interface Table {
String value();
}
2、定義@Column注解
@Target(ElementType.FIELD) // 注解的作用范圍 作用在字段上
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期 運(yùn)行時(shí)
public @interface Column {
String value(); // 只有一個(gè)成員,則定義成value,在使用的時(shí)候無(wú)參使用參數(shù)名和賦值符號(hào)=
}
3殿雪、定義實(shí)體類(lèi)
/**
* 用戶的實(shí)體類(lèi)
*
* @author YUBIN
*/
@Table("tb_user")
public class User {
@Column("id")
private Long id;
@Column("user_name")
private String userName;
@Column("sex")
private Integer sex;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
}
4暇咆、定義UserDao
/**
* 用戶的持久層
*
* @author YUBIN
*/
public class UserDao {
public String query(User user) {
StringBuffer sb = new StringBuffer("select * from ");
Class c = User.class;
// 獲取類(lèi)上面的Table注解
boolean isExistTableName = c.isAnnotationPresent(Table.class);
if (!isExistTableName) {
return null;
}
// 獲取表名
Table table = (Table) c.getAnnotation(Table.class);
sb.append(table.value()).append(" where 1=1");
Field[] fields = c.getDeclaredFields();
if (fields != null && fields.length > 0 && user != null) {
for (Field field : fields) {
// 獲取字段上的Column注解
boolean isExistField = field.isAnnotationPresent(Column.class);
if (isExistField) {
// 判斷該字段是否有值
// 獲取getXxx方法
String getMethodName = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
try {
Method method = c.getMethod(getMethodName);
Object result = method.invoke(user, null);
if (result != null) {
Column column = field.getAnnotation(Column.class);
sb.append(" and " + column.value());
if (result instanceof String) {
sb.append("='").append(result).append("'");
} else {
sb.append("=").append(result);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return sb.toString();
}
}
5、測(cè)試類(lèi)
/**
* 測(cè)試類(lèi)
*
* @author YUBIN
*/
public class Test {
public static void main(String[] args) {
UserDao userDao = new UserDao();
User user = new User();
user.setId(1L);
user.setUserName("zhangsan");
System.out.println(userDao.query(user));
}
}
6丙曙、測(cè)試結(jié)果
七爸业、 結(jié)語(yǔ)
希望我的這篇文章能對(duì)大家理解注解、使用注解并且能夠自定義注解有所幫助亏镰,如有疑問(wèn)可在評(píng)論區(qū)交流沃呢,謝謝大家的支持。