基本概念:註解
,顧名思義,就是對某一事物進行添加註釋說明
,會存放一些信息,這些信息可能對之後某個時段來講是頗有用處的。Java 註解
(Annotation)又稱 Java 標註
,是 JDK5.0 引入的一種註釋機制。Java 語言中的類
、方法
、變量
、參數
和包
等均可以被標註(添加某些信息)。在編譯器生成類文件時,標註能夠被嵌入到字節碼中。Java 虛擬機能夠保留標註內容,在運行時能夠經過反射的方式獲取到標註內容 。 固然它也支持自定義 的Java 標註。java
註解與註釋的區別程序員
定義不一樣
:註解與類、接口在同一層次的,是一種描述數據的數據,能夠理解爲註解就是源代碼的元數據。註釋則是對源代碼的介紹,方便開發者理解代碼的所撰寫的文字。數組
做用不一樣
:註解是Java 編譯器能夠理解的部分,是給編譯器看的。經過標記包、類、字段、方法、局部變量、方法參數等元數據,告訴jvm這些元數據的信息。註釋是程序員對源代碼作一些記憶或提示性描述,是給人來看的。它能告訴開發者這段代碼的邏輯、說明、特色等內容,對代碼起到解釋、說明的做用。markdown
使用範圍不一樣
:使用範圍不一樣:註解 ,參與代碼編譯,以@開頭的,與工具一塊兒使用。對於位置、語法、內容有必定的限制。註釋 ,能夠隨意在任務位置填寫內容,對代碼任何沒有影響。jvm
總之,註解
能夠理解爲對類、變量、方法和接口進行規範和約束,註釋
則理爲開發者對代碼進行解釋而撰寫的文字。ide
註解能夠根據來源能夠分爲系統註解
、自定義註解
和第三方註解
,系統註解根據用途能夠分爲內置註解
和元註解
,在下面的文章中,咱們主要講解內置註解
、元註解
和自定義註解
。工具
在java.lang包下存在着咱們常常看到的註解,分別是@Deprecated
、@Override和
和@SuppressWarnings
測試
@Deprecated
能夠修飾類
、方法
和變量
,被@Deprecated修飾後表示不建議使用,它的存在僅僅是爲了兼容之前的程序,因爲不能直接把它拋棄,因此將它設置爲過期。可是被這個註解修飾的類、方法在高版本的JDK中使用時了可能會出現錯誤。this
源代碼以下編碼
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
複製代碼
其中@Documented、@Retention和@Target是元註解
,咱們在下文介紹
它代表了被註解的方法須要重寫
父類中的方法,若是某個方法使用了該註解,卻沒有覆寫超類中的方法,編譯器就會報出錯誤。在子類中重寫父類或接口的方法,@Overide
並非必須的。可是仍是建議使用這個註解,由於在某些狀況下,假設你修改了父類的方法的名字,那麼以前重寫的子類方法將再也不屬於重寫,若是沒有@Overide,你將不會察覺到這個子類的方法。有了這個註解修飾,編譯器則會提示你這些信息。
源代碼以下
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
複製代碼
@SuppressWarnings
用來抑制編譯器生成警告信息,能夠修飾的元素爲類
,方法
,方法參數
,屬性
,局部變量
。它能夠達到抑制
編譯器編譯時產生警告的目的,使用@SuppressWarnings註解,採用就近原則
,好比一個方法出現警告,儘可能使用@SuppressWarnings註解這個方法,而不是註解方法所在的類。所屬範圍越小越好,由於範圍大了,不利於發現該類下其餘方法的警告信息。 可是咱們一般不建議使用@SuppressWarnings註解,使用此註解,開發人員看不到編譯時編譯器提示的相應的警告,不能選擇更好、更新的類、方法或者不能編寫更規範的編碼。同時後期更新JDK、jar包等源碼時,使用@SuppressWarnings註解的代碼可能受新的JDK、jar包代碼的支持,出現錯誤,仍然須要修改。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
複製代碼
在上面的代碼中,咱們看到了註解上還有註解,這種修飾註解的註解被稱爲元註解。事實上,元註解
(meta-annotation)的做用就是註解其它的註解,Java在java.lang.annotation包中定義了4個標準的元註解類型,分別爲@Target
、@Retention
、@Documented
和@Inherited
@Target
用於描述註解的使用範圍,即被描述的註解能夠用到什麼地方,@Target註解內定義了ElemenType[]
數組,數組以枚舉類的形式定義了註解的修飾範圍。經過下面的源代碼咱們能夠看出,該註解可以用於類
、接口
、構造器
、屬性
和方法
、參數聲明
、註解聲明
等
源代碼以下
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
複製代碼
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
}
複製代碼
@Rentention
表示須要在什麼級別保存該註釋信息,用於描述註解的生命週期
,經過源代碼,咱們能夠看出,註解內有個RetentionPolicy
的值,咱們繼續深刻往下看,RetentionPolicy
是個枚舉類型的值,它有三個值可供選擇,SOURCE
是在源代碼層面,在編譯
是將會失效;CLASS
做用在class文件
中,可是在運行時失效
;RUNTIME
在運行時依舊保留該註解,所以能夠經過反射機制來讀取註解內的信息。所以,這三個值對應的生命週期大小爲:SOURCE<CLASS<RUNTIME
,若是不手動添加的話,則默認爲CLASS。
源代碼以下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/** * Returns the retention policy. * @return the retention policy */
RetentionPolicy value();
}
複製代碼
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. */
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. * * @see java.lang.reflect.AnnotatedElement */
RUNTIME
}
複製代碼
@Documented
代表該註解將被包含在javadoc
中。該註解用的相對較少。
源代碼以下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
複製代碼
@Inherited
說明子類能夠繼承父類中的該註解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
複製代碼
經過使用元註解的組合,咱們能夠按照本身的需求來自定義註解,咱們一般使用@interface
來自定義註解,它自動繼承了java.lang.annotation.Annotation
接口。接下,咱們來以一段代碼仔細分析下如何自定義註解:
public @interface MyAnnotation{
}
複製代碼
註解一
是一個最簡單的自定義註解,咱們能夠看到,它以public
修飾,以@interface
用來聲明一個註解,具體的格式爲:public @interface 註解名{定義內容}
,若是要在註解內添加一個參數,該怎樣定義呢?
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotantion3{
//參數名爲vale,當註解內只有一個參數,使用註解時,參數名可省略
String value();
}
複製代碼
特別須要注意的是,註解內的方法名稱就是參數的名稱,而返回值類型就是參數類型,咱們能夠這樣使用
@Retention(value = RetentionPolicy.RUNTIME)
@myAnnotantion3("snow")
public void test2(){
}
複製代碼
由於註解內只有一個參數,因此在使用註解時,參數名稱是能夠省略的。
若是,咱們咱們想添加多個的參數值,該怎麼自定義註解呢
Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotaion2{
/** *表示註解的參數,name參數名,String 表示參數的類型 * 參數加default,在註解內可寫可不寫 */
String name();
int age();
int id();
}
複製代碼
同理,咱們只須要在相應的位置引用該註解便可
@myAnnotaion2(name = "Simon",age=25,id=23)
public void test1(){
}
複製代碼
若是咱們向給註解內的參數設定默認值,咱們能夠這樣作
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface myAnnotaion2{
/** *表示註解的參數,name參數名,String 表示參數的類型 * 參數加default,在註解內可寫可不寫 */
String name() default "";
int age() default 0;
int id() default -1;
}
複製代碼
在實際的方法使用中,咱們只須要給必需要修改的值賦值便可
@myAnnotaion2(name = "Simon")
public void test1(){
System.out.println("測試註解1");
}
複製代碼
所以,自定義註解能夠概括以下:
@interface
用來聲明一個註解,格式
:public @interface 註解名{定義內容}
其中每個方法實際上聲明瞭一個配置參數
方法的名稱
就是參數的名稱
返回值類型
就是參數的類型
(返回值只能時基本類型,Class,String,enum)
能夠經過default
用來聲明參數的默認值
若是隻有一個參數成員
,通常參數名爲value
註解元素必需要有值,咱們定義註解元素時,常用空字符串0做爲默認值
在以上的講解中,咱們使用註解都是對所修飾的類、方法、變量進行規範和約束
,在大多數使用場景中,以方法
爲例,咱們須要將註解中的信息同方法聯繫起來,即將註解中的參數信息的注入到方法中。
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation5 {
String name() default " ";
int age() default 0;
}
複製代碼
@MyAnnotation5(name = "Simon", age = 25)
public void testInjectValue(String name,int age) {
System.out.println("獲取註解中的參數值:");
System.out.println(name);
System.out.println(age);
}
複製代碼
- 反射獲取該類的方法
- 經過方法獲取註解中的參數值
- 將註解中的參數值注入到相應的方法中
//反射獲取類,並獲得類中的方法
Class aClass = InjectValue.class;
Method method=aClass.getMethod("testInjectValue",String.class,int.class);
//獲取註解中的屬性值
MyAnnotation5 myAnnotation5=method.getAnnotation(MyAnnotation5.class);
String name=myAnnotation5.name();
int age=myAnnotation5.age();
//將屬性值注入到相應的方法中
Object o=aClass.newInstance();
method.invoke(o,name,age);
複製代碼
前面咱們講解如何將註解中的參數爲基本數據類型注入到方法中,那麼如何將註解中的參數爲對象注入到方法中呢?
public class Animal {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
複製代碼
public class AnimalDao {
private Animal animal;
public Animal getAnimal() {
return animal;
}
@MyAnnotation6(name = "Dog",age = 12)
public void setAnimal(Animal animal) {
this.animal = animal;
}
}
複製代碼
public class TestInjectObject {
public static void main(String[] args) throws Exception {
//1.使用PropertyDescriptor獲得想要注入的屬性
PropertyDescriptor descriptor = new PropertyDescriptor("animal", AnimalDao.class);
//2.獲得要想注入屬性的具體對象
Animal animal = (Animal) descriptor.getPropertyType().newInstance();
//3.獲得該屬性的寫方法
Method method = descriptor.getWriteMethod();
//4.獲得寫方法的註解
Annotation annotation = method.getAnnotation(MyAnnotation6.class);
//5.獲得註解上的信息
Method[] methods = annotation.getClass().getMethods();
//6.將註解上的信息填充到animal對象上
for (Method m : methods) {
//獲得註解上屬性的名字
String name = m.getName();
//看看animal對象有沒有與之對應的方法
try {
PropertyDescriptor descriptor1 = new PropertyDescriptor(name, Animal.class);
Method method1 = descriptor1.getWriteMethod();
//獲得註解中的值
Object o = m.invoke(annotation, null);
//調用animal對象的setter方法,將註解上的值設置進去
method1.invoke(animal, o);
} catch (Exception e) {
continue;
}
}
AnimalDao animalDao = new AnimalDao();
method.invoke(animalDao, animal);
System.out.println(animalDao.getAnimal().getName());
System.out.println(animalDao.getAnimal().getAge());
}
}
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation6 {
String name();
int age();
}
複製代碼
所以,將對象注入到方法中能夠總結以下