### 1.簡介
注解(也被稱為元數據),為我們在代碼中添加信息提供了一種形式化的方法。注解在一定程度上是把元數據與源代碼文件結合在一起,而不是保存在外部文檔中這一大趨勢之下所催生的。
它可以提供用來完整的描述程序所需的信息,而這些信息是無法使用Java來表達的。因此,注解使得我們能夠以將編譯器來測試和驗證的格式,存儲有關程序的額外信息。注解可以用來生成描述符文件,甚至是新的類定義。通過使用注解,我們可以將這些元數據保存在Java源代碼中,并利用Annotation API為自己的注解構造處理工具。
注解可以生成更加干凈易讀的代碼以及編譯器類型檢查等等。
注解(annotation)實在實際的源代碼級別保存所有的信息,而不是某種注釋性文字(comment),這使得代碼更加簡潔,便于維護。
### 2.注解分類
| 按照運行機制分類 | 描述 |
|-----|-----|
| 源碼注解 | 注解只在源碼中存在,編譯成.class文件就不存在了 |
| 編譯時注解 | 注解只在源碼和.class文件中都存在(例如:@override) |
| 運行時注解 | 在運行階段還起作用,甚至影響運行邏輯的注解(例如:@Autowired) |
### 3.內置注解:
(1)@override
表示當前的方法定義將覆蓋超類中的方法。如果你不小心拼寫錯誤,或者方法簽名對不上被覆蓋的方法,編譯器就會發出錯誤提示。
(2)@Deprecated
如果程序員使用了注解為它的元素,那么編譯器會發出警告信息。
(3)@SuppressWarnings
關閉不當的編譯器警告信息(在java SE5 之前,也可以使用這個注解,不過被忽略不起作用)
### 4.基本語法
4.1 定義注解
可以看到注解的定義很像接口的定義。事實上,與其他任何Java接口一樣,注解也會被編譯成class文件。
~~~
package com.qunar.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Annotation {
// 定義Description注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
// 使用@interface 關鍵字定義注解
public @interface Description{
// 成員以無參無異常方式聲明
String desc();
String author();
// 可以使用default關鍵字為成員指定一個默認值
int age() default 18;
}
}
~~~
除了@符號以外,@Description的定義很像一個接口。定義注解的時候會需要一些元注解,如@Target和@Retention。@Target用來定義你的注解將用于什么地方(是一個方法上還是一個類上),@Retention用來定義該注解在哪一個級別上可用(在源代碼上或者是類文件上或者是運行時),具體下面講解。
4.2 注解元素
注解@Description中包含int元素age,以及String元素desc和author。注解元素可以使用的類型如下:
- 所有基本數據類型(int,float,boolean等)
- String
- Class
- enum
- Annotation
- 以上類型的數組
如果你使用了其他類型,那么編譯器就會報錯。注意,也不允許使用任何包裝類型,不過由于自動打包的存在,這算不上什么限制。注解也可以作為元素的類型,也就是注解可以嵌套。
4.3 默認值限制
編譯器對元素的默認值有些過分的挑剔。
首先,元素不能有不確定的值,也就是說元素必須要么有默認值,要么使用注解時提供元素的值。
其次,對于非基本類型的元素,無論是在源代碼中聲明時,或者是在注解接口中定義默認值時,都不能以null作為其值。為了這個約束,我們只能自己定義一些特殊的值,例如空字符串或者負數,來表示某個元素不存在。
4.4 元注解
元注解只負責注解其他的注解。
<table cellspacing="0" cellpadding="0" style="margin:8px 0px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:892px"><tbody><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); text-align:center; width:206px">元注解</td><td colspan="2" rowspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); text-align:center; width:429px"><span style="font-size:10.5pt; line-height:1.5">參數</span></td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); text-align:center; width:206px">描述</td></tr><tr><td colspan="1" rowspan="7" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"><br/><br/><br/>@Taget<br/><br/><br/></td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">CONSTRUCTOR</td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">構造器的聲明</td><td colspan="1" rowspan="6" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"><br/><br/>? ? ? ? ? ?表示注解可以用于什么地方<br/><br/><br/></td></tr><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">FIELD</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">域聲明</td></tr><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">METHOD</td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">方法聲明</td></tr><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">PACKAGE</td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">包聲明</td></tr><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">PARAMETER</td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">參數聲明</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">TYPE</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">類,接口或enum聲明</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">LOCAL_VARIABLE</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">局部變量聲明</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">?</td></tr><tr><td colspan="1" rowspan="3" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"><br/>@Retention<br/></td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">SOURCE</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">注解只在源碼中存在,編譯成.class文件就不存在了</td><td colspan="1" rowspan="3" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"><br/>? ? ? ? 表示需要在什么級別保存該注解信息<br/></td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">CLASS</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">注解只會在.class文件存在,會被VM丟棄</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">RUNTIME</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">VM將在運行期也保留注解,因此可以通過反射機制讀取注解的信息</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">@Document</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">?</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">?</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">將此注解包含在Javadoc中</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">@Inherited</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">?</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">?</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">允許子類繼承父類中的注解</td></tr></tbody></table>
4.5 使用注解
語法:@<注解名稱>(<成員名1> = <成員值1>,<成員名2> = <成員值2>,...)
~~~
package com.qunar.annotation;
import com.qunar.annotation.Annotation.Description;
public class Student {
private String name;
@Description(desc = "set name for student object" , author = "sjf0115")
public String getName() {
return name;
}
@Description(desc = "get name from student object" , author = "sjf0115", time = "2016-01-11")
public void setName(String name) {
this.name = name;
}
}
~~~
### 5.解析注解
通過反射機制獲取類,函數或者成員上的運行時注解信息,從而實現動態控制程序運行的邏輯。
~~~
package com.qunar.annotation;
import java.lang.reflect.Method;
import com.qunar.annotation.Annotation.Description;
public class ParseAnnotation {
public static void main(String[] args){
Class<?> class1 = null;
try {
// 使用類加載器加載類
class1 = Class.forName("com.qunar.annotation.Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 判斷Student類上是否有Description注解
boolean isExits = class1.isAnnotationPresent(Description.class);
if(isExits){
// 注解實例
Description desc = class1.getAnnotation(Description.class);
System.out.println("注解:" + desc.toString());
}//if
// 獲取Student類上的所有方法
Method[] methods = class1.getMethods();
// 遍歷所有方法
for (Method method : methods) {
// 判斷方法上是否有Description注解
isExits = method.isAnnotationPresent(Description.class);
if(isExits){
Description description = method.getAnnotation(Description.class);
System.out.println("方法注解:" + description.toString());
}//if
}//for
}
}
~~~
運行結果:
<table cellspacing="0" cellpadding="0" style="margin:8px 0px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:892px"><tbody><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:875px"><br/><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微軟雅黑">方法注解:@com.qunar.annotation.Annotation$Description(time=2016-01-12,?desc=set?name?for?student?object,?author=sjf0115)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微軟雅黑">方法注解:@com.qunar.annotation.Annotation$Description(time=2016-01-11,?desc=get?name?from?student?object,?author=sjf0115)</span></div></td></tr></tbody></table>
~~~
package com.qunar.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import com.qunar.annotation.Annotation.Description;
public class ParseAnnotation {
public static void main(String[] args){
Class<?> class1 = null;
try {
// 使用類加載器加載類
class1 = Class.forName("com.qunar.annotation.Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 判斷Student類上是否有Description注解
boolean isExits = class1.isAnnotationPresent(Description.class);
if(isExits){
// 注解實例
Description desc = class1.getAnnotation(Description.class);
System.out.println("注解:" + desc.toString());
}//if
// 獲取Student類上的所有方法
Method[] methods = class1.getMethods();
// 遍歷所有方法
for (Method method : methods) {
// 方法上獲取所有的注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
if(annotation instanceof Description){
System.out.println("Description注解:" + annotation.toString());
}//if
}//for
}//for
}
}
~~~
這兩個程序都用到了反射的方法:getMethods()和getAnnotation(),它們都屬于AnnotatedElement接口(Class,Method與Field等類都實現了該接口)。getAnnotation()方法返回指定類型的注解對象,在這里就是Description。如果被注解的方法上沒有該類型的注解,則返回null值。
- 前言
- [Hibernate開發之路](1)Hibernate配置
- [Hibernate開發之路](2)Hibernate問題
- [Hibernate開發之路](3)基礎配置
- [Hibernate開發之路](4)ID生成策略
- [Hibernate開發之路](5)聯合主鍵
- [設計模式實踐之路](1)單例模式
- [Java]UDP通信的簡單例子
- [Java]套接字地址InetAddress講解
- [Java開發之路](1)final關鍵字
- [Java開發之路](2)Java字符串
- [Java開發之路](3)Java常用類
- [Java開發之路](4)String、StringBuffer與StringBuilder詳解
- [Java開發之路](5)異常詳解
- [Java開發之路](6)File類的使用
- [Java開發之路](7)RandomAccessFile類詳解
- [Java開發之路](8)輸入流和輸出流
- [Java開發之路](9)對象序列化與反序列化
- [Java開發之路](10)DOM解析XML文檔
- [Java開發之路](11)SAX解析XML文檔
- [Java開發之路](12)JDOM和DOM4J解析XML文檔
- [Java開發之路](14)反射機制
- [Java開發之路](15)注解
- [Java開發之路](16)學習log4j日志
- [Java開發之路](18)關于Class.getResource和ClassLoader.getResource的路徑問題
- [Java開發之路](19)Long緩存問題
- [Java開發之路](20)try-with-resource 異常聲明
- [Java開發之路](21)Comparator與Comparable
- [Java]Java工程師成神之路
- [細說Java](1)圖說字符串的不變性
- [細說Java](2)Java中字符串為什么是不可變的
- [細說Java](3)創建字符串是使用&quot; &quot;還是構造函數?