注解
?一般情況下,注解是用來(lái)給程序員、編譯器提示的,就比如 @NoNull 就是用來(lái)提示不為空,在null時(shí),編譯器會(huì)根據(jù)注解報(bào)出相關(guān)的異常。
?但是注釋還有一個(gè)很重要的作用就是和反射一起使用,我們可以通過(guò)反射拿到被注釋的字段、方法、類,當(dāng)然也可以拿到注釋中的信息。
?還是先說(shuō)用來(lái)注解注解的注解(原生注解),哈哈哈!
@Target
?表示該注解可以用于什么地方,可能的ElementType參數(shù)有:
- CONSTRUCTOR:構(gòu)造器的聲明
- FIELD:域聲明(包括enum實(shí)例)
- LOCAL_VARIABLE:局部變量聲明
- METHOD:方法聲明
- PACKAGE:包聲明
- PARAMETER:參數(shù)聲明
- TYPE:類、接口(包括注解類型)或enum聲明
@Retention
?表示需要在什么級(jí)別保存該注解信息??蛇x的RetentionPolicy參數(shù)包括:
- SOURCE:注解將被編譯器丟棄
- CLASS:注解在class文件中可用,但會(huì)被VM丟棄
- RUNTIME:VM將在運(yùn)行期間保留注解,因此可以通過(guò)反射機(jī)制讀取注解的信息。
@Document
?將注解包含在Javadoc中。
@Inherited
?允許子類繼承父類中的注解。
?看完了若是一臉懵,請(qǐng)看注解的構(gòu)造方式:
//這里就實(shí)現(xiàn)了MyAnnotationTest這個(gè)注解的構(gòu)建
//這里代表該注解是用來(lái)注解方法、并且可以被反射獲取信息
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotationTest {
String name() default " ";
int id()default 0;
}
?注解的使用:
//該注解的使用:
//賦值
@MyAnnotationTest(name = "小明" ,id = 1)
public void annnotationMethod(){
System.out.println("這是一個(gè)被注解的方法");
}
?是不是感覺(jué)這里看不出來(lái)什么特殊含義。但是,假如說(shuō),對(duì)我自己,這個(gè)注解代表被注解的方法是用來(lái)干某些耗時(shí)操作的,那么是不是就有意義了?
?當(dāng)然這樣使用還是太淺層了,我們來(lái)結(jié)合反射試試!
反射
?我們還是先不談注解和反射搭配好伐?先單獨(dú)談?wù)劮瓷洌?strong>JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為Java語(yǔ)言的反射機(jī)制。
?首先定義了一個(gè)Student類,這個(gè)類是我們想象出來(lái)的(可以啥子都有),專門用來(lái)接下來(lái)實(shí)現(xiàn)反射用的。一般情況下,我們是在對(duì)這個(gè)類一點(diǎn)都不了解的情況下才會(huì)想著去獲取它的信息,好奇心按捺不住了啊?。?!
?在另外的類里面獲取Student的信息是這樣實(shí)現(xiàn)的:
//先獲取類的對(duì)象
private static Class<?> c;
static {
try {
c = Class.forName("com.example.hp.javaclasstest.reflection.Student");
//另外一種方法是傳遞當(dāng)前類的對(duì)象過(guò)去,使用object.getClass()獲取到類對(duì)象。
} catch (ClassNotFoundException e) {
c = null;
}
}
//這里就是獲取到所有的構(gòu)造函數(shù),是可以直接將它打印出來(lái)的
Constructor<?>[] constraints = c.getConstructors();
//這里是獲取到某一個(gè)方法
try {
Method method = c.getMethod("showMyData",null);
//這個(gè)null是指該方法的返回對(duì)象,如果是String類型,則是String.class
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
?其它的字段、方法、類的獲取方式待自己慢慢探索吧。
?但是萬(wàn)一有的小伙伴手癢,又想使用它拿到的方法該怎么辦呢?
?沒(méi)關(guān)系,上熱騰騰的代碼:
//獲取到這個(gè)類的單例
Object object = null;
try {
object = c.getConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//實(shí)現(xiàn)某個(gè)方法的使用
//object:單例,null:該方法傳入?yún)?shù),無(wú)則null
method.invoke(object,null);
是不是感受出來(lái)什么了?通過(guò)反射獲取一個(gè)類的信息就是這么簡(jiǎn)單!
其實(shí)在字段或者方法上面加上注解也就是比普通的特殊了那么一丟丟,我們稍加判斷同樣是能夠獲取到被注解了的對(duì)象。
筆者手懶,就通通使用上面已經(jīng)建好了的對(duì)象。
上代碼?。?!
//獲取到Student的所有公開(kāi)方法
Method[] methods = c.getMethods();
//注意,請(qǐng)仔細(xì)看這里?。?!
for(Method m:methods){
//判斷方法是否被MyAnnotationTest這個(gè)注解類注解
if(m.isAnnotationPresent(MyAnnotationTest.class)){
//這個(gè)m就是被 MyAnnotationTest 注釋了的方法
//我們直接將它拿出來(lái)吧,return出來(lái)。
return m;
}
}
先緩口氣,我們邁出了很大一步,我們已經(jīng)獲取到了被注釋了的方法。接下來(lái)就是按照我們自己的需求來(lái)寫了。
假如,我只是要實(shí)現(xiàn)這個(gè)方法,那么就可以采用invoke()方法去實(shí)現(xiàn)啦。但是我要想知道注釋里面的信息呢(例如上面的"小明",1)。
這也是超級(jí)簡(jiǎn)單的:
//獲取到注釋類對(duì)象
MyAnnotationTest annotation = m.getAnnotation(MyAnnotationTest.class);
//獲取到我的"小明 "
String name = annotation.name;
輕而易舉實(shí)現(xiàn)啦。
總結(jié)
Java的注解和反射搭配使用是有很大作用的,這兩者搭配是可以實(shí)現(xiàn)超大程度的解耦。我在這個(gè)類里面實(shí)現(xiàn)了注解,我可以在另外一個(gè)毫不相關(guān)的類里面去獲得這個(gè)類的內(nèi)容,并且實(shí)現(xiàn)里面的方法。
很多好的三方庫(kù)的都是實(shí)現(xiàn)的注解和反射,例如Dagger2、Retrofit、ButterKnife...但是這些第三庫(kù)不是這么簡(jiǎn)單去實(shí)現(xiàn)的,它們會(huì)生成工具類來(lái)快速實(shí)現(xiàn)信息獲取。
但是在Java中實(shí)現(xiàn)反射可是一件“費(fèi)勁”的事兒,是比較消耗CPU資源的。所以,請(qǐng)勿濫用反射!