原文出處: localityhtml
1.可以讀懂別人寫的代碼,特別是框架相關的代碼。java
2.原本可能須要不少配置文件,須要不少邏輯才能實現的內容,就可使用一個或者多個註解來替代,這樣就使得編程更加簡潔,代碼更加清晰。面試
3.(重點)另眼相看。
(可是怎麼樣才能讓別人另眼相看呢?會用註解不是目的,最重要的是要使用自定義註解來解決問題。)
舉個栗子:
若是面試的時候,你跟老闆說你會使用註解,老闆以爲你這我的還行;可是若是老闆發現你會自定義註解解決問題,老闆確定就會眼前一亮。編程
註解這一律念是在java1.5版本提出的,說Java提供了一種原程序中的元素關聯任何信息和任何元數據的途徑的方法。框架
1)JDK註解
JDK註解一共分爲三類:eclipse
案例:
咱們先新建一個接口people,以下:ide
1
2
3
4
5
|
public
interface
people {
public
String name();
public
int
age();
public
void
work();
}
|
而後再建一個類Child實現類people這個接口,並實現該類的方法:函數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
class
Child
implements
people {
@Override
public
String name() {
return
null
;
}
@Override
public
int
age() {
return
0
;
}
@Override
public
void
work() {
}
|
看到這裏,咱們發現這裏的全部方法都會加上一個@Override標記,它告訴咱們,同時也告訴編譯器咱們的這些方法確定覆蓋了類people裏面的方法的。假如說,我如今把類people裏面的某一個方法註釋掉:測試
1
|
//public String name();
|
再看類Child裏面的name方法就會報錯。這樣,之後你們看到@Override的時候就能想到這個方法是覆蓋了某個接口的方法的。ui
而後,咱們回過頭來看類people裏面有一個work的方法。這裏咱們能夠理解爲人是要工做的,可是並非全部的人都在工做,那麼怎麼辦呢?若是說這個接口正在用,咱們不能刪除這個方法,這個時候咱們就能夠這樣:
1
2
|
@Deprecated
public
void
work();
|
@Deprecated標記就代表這個方法已通過時了,在實際中,它又有什麼樣的應用場景呢?咱們在建一個測試類:
1
2
3
4
5
6
|
public
class
Test {
public
void
work() {
people people=
new
Child();
! people.work();
}
}
|
這個時候咱們會發現myeclipse會給一個警告,而且在work中間出現一個破折號,意思就是這個方法已通過時了。那麼問題來了,雖然這個方法過期了,可是咱們就是那麼傲嬌,必定要用它,怎麼辦呢?只須要這樣:
1
2
3
4
5
6
7
|
public
class
Test {
@SuppressWarnings
(
"deprecation"
)
public
void
work() {
people people=
new
Child();
people.work();
}
}
|
這樣咱們就忽略了這個警告。@SuppressWarnings(「deprecation」)就表示咱們忽略了deprecation這樣的一個警告。
2)Java第三方註解
1)按照運行機制劃分:
【源碼註解→編譯時註解→運行時註解】
源碼註解:只在源碼中存在,編譯成.class文件就不存在了。
編譯時註解:在源碼和.class文件中都存在。像前面的@Override、@Deprecated、@SuppressWarnings,他們都屬於編譯時註解。
運行時註解:在運行階段還起做用,甚至會影響運行邏輯的註解。像@Autowired自動注入的這樣一種註解就屬於運行時註解,它會在程序運行的時候把你的成員變量自動的注入進來。
2)按照來源劃分:
【來自JDK的註解——來自第三方的註解——自定義註解】
3)元註解:
元註解是給註解進行註解,能夠理解爲註解的註解就是元註解。
咱們分四步來解析自定義註解:
自定義註解的語法要求:
1
2
3
4
5
6
7
8
9
|
@Target
({ElementType.METHOD,ElementType.TYPE})
@Retention
(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public
@interface
Description {
String desc();
String author();
int
age()
default
18
;
}
|
首先咱們要明確這不是一個接口,它是使用@interface關鍵字定義的一個註解。
而後咱們看下面的幾個方法,String desc();
雖然它很相似於接口裏面的方法,其實它在註解裏面只是一個成員變量(成員以無參無異常的方式聲明),int age() default 18;
(成員變量能夠用default指定一個默認值的)。
最後咱們要知道:
①.成員類型是受限制的,合法的類型包括基本的數據類型以及String,Class,Annotation,Enumeration等。
②.若是註解只有一個成員,則成員名必須取名爲value(),在使用時能夠忽略成員名和賦值號(=)。
③.註解類能夠沒有成員,沒有成員的註解稱爲標識註解。
元註解:
有沒有發現上面那段代碼有一個沒有說呢?沒錯,它們就是咱們所說的元註解:
1
2
3
4
|
@Target
({ElementType.METHOD,ElementType.TYPE})
@Retention
(RetentionPolicy.RUNTIME)
@Inherited
@Documented
|
咱們先看第一行:@Target是這個註解的做用域,ElementType.METHOD
是這個註解的做用域的列表,METHOD
是方法聲明,除此以外,還有:CONSTRUCTOR(構造方法聲明),FIELD(字段聲明),LOCAL VARIABLE(局部變量聲明),METHOD(方法聲明),PACKAGE(包聲明),PARAMETER(參數聲明),TYPE(類接口)
第二行:@Retention是它的生命週期,前面不是說註解按照運行機制有一個分類嘛,RUNTIME
就是在運行時存在,能夠經過反射讀取。除此以外,還有:SOURCE(只在源碼顯示,編譯時丟棄),CLASS(編譯時記錄到class中,運行時忽略),RUNTIME(運行時存在,能夠經過反射讀取)
第三行:@Inherited是一個標識性的元註解,它容許子註解繼承它。
第四行:@Documented,生成javadoc時會包含註解。
使用自定義註解:
使用註解的語法:
@<註解名>(<成員名1>=<成員值1>,<成員名1>=<成員值1>,…)
案例:
1
2
3
4
|
@Description
(desc=
"i am Color"
,author=
"boy"
,age=
18
)
public
String Color() {
return
"red"
;
}
|
這裏的Description是咱們剛纔在自定義註解語法要求裏面定義的註解噢,而後咱們能夠給它的每個成員變量賦值,注意數據類型。值得注意的是,由於咱們前面定義的做用域是在方法和類接口上,因此這個註解在Color()方法上使用是沒問題的。
解析註解
概念:
經過反射獲取類 、函數或成員上的運行時註解信息,從而實現動態控制程序運行的邏輯。
接下來,咱們就開始測試了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
ParseAnn {
public
static
void
main(String[] args) {
try
{
// 使用類加載器加載類
Class c = Class.forName(
"com.test.Child"
);
// 找到類上面的註解
boolean
isExist = c.isAnnotationPresent(Description.
class
);
// 上面的這個方法是用這個類來判斷這個類是否存在Description這樣的一個註解
if
(isExist) {
// 拿到註解實例,解析類上面的註解
Description d = (Description) c.getAnnotation(Description.
class
);
System.out.println(d.value());
}
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
}
}
|
輸出的結果:i am class annotation
能夠看到,咱們成功的解析了Child類上面的註解。
接下來,咱們繼續解析方法上的註解:
1
2
3
4
5
6
7
8
9
10
|
//獲取全部的方法
Method[] ms = c.getMethods();
// 遍歷全部的方法
for
(Method m : ms) {
boolean
isExist1 = m.isAnnotationPresent(Description.
class
);
if
(isExist1) {
Description d1=m.getAnnotation(Description.
class
);
System.out.println(d1.value());
}
}
|
輸出的結果:i am class annotation
i am method annotation
能夠看到,咱們成功的解析了方法上面的註解。
1
2
3
4
5
6
7
8
9
10
11
12
|
//另外一種解析方法
for
(Method m : ms) {
//拿到方法上的全部的註解
Annotation[] as=m.getAnnotations();
for
(Annotation a : as) {
//用二元操做符判斷a是不是Description的實例
if
(a
instanceof
Description) {
Description d=(Description) a;
System.out.println(d.value());
}
}
}
|
也能夠獲得上面的效果。
此時,若是把Description類裏面的元註解改一下,好比:
@Retention(RetentionPolicy.RUNTIME)→@Retention(RetentionPolicy.SOURCE),再運行程序,結果會成怎樣呢?若是改爲CLASS呢?讀者們要不要試一試?
若是看過我寫的《談談JAVA反射機制》——Class類的動態加載的讀者,仔細想一下咱們這個環境,就知道爲何了。