1. 程式人生 > >【框架基礎】:全面解析Java註解(一)

【框架基礎】:全面解析Java註解(一)

       Java註解概述

       要了解Java註解要先知道Java的反射,反射是執行時獲取類的成員,註解也是類的成員,以此達到動態編碼的效

果,多用在框架,或者使用框架時候添加註解讓框架呼叫。

       註解定義:註解(Annotation),也叫元資料。Java提供了一種原程式中的元素關聯任何資訊和任何元資料的途

徑和方法,一種程式碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、介面、列舉是在同一個層次。它可

以宣告在包、類、欄位、方法、區域性變數、方法引數等的前面,用來對這些元素進行說明,註釋。

       Annotation(註解)是JDK1.5及以後版本引入的。它可以用於建立文件,跟蹤程式碼中的依賴性,甚至執行基本編譯

時檢查。註解是以‘@註解名’在程式碼中存在的,根據註解引數的個數,我們可以將註解分為:標記註解、單值注

解、完整註解三類。它們都不會直接影響到程式的語義,只是作為註解(標識)存在,我們可以通過反射機制程式設計實

現對這些元資料(用來描述資料的資料)的訪問。另外,你可以在編譯時選擇程式碼裡的註解是否只存在於原始碼級,

或者它也能在class檔案、或者執行時中出現(SOURCE/CLASS/RUNTIME)。

       註解的作用

       如果要對於註解的作用進行分類,還沒有明確的定義,不過我們可以根據它所起的作用,大致可分為三類:

       編寫文件:通過程式碼裡標識的元資料生成文件【生成文件doc文件】

       程式碼分析:通過程式碼裡標識的元資料對程式碼進行分析【使用反射】

       編譯檢查:通過程式碼裡標識的元資料讓編譯器能夠實現基本的編譯檢查【Override】

為什麼學習註解

       1、能夠讀懂別人寫的程式碼,特別是框架相關的程式碼,增加程式碼的閱讀性;

       2、讓程式設計更加簡潔,程式碼更加清晰,理清自己的思路;

       3、生成API文件;

       Java中的常見註解

       JDK內建註解

       

       @Override

       它的作用是對覆蓋超類中方法的方法進行標記,如果被標記的方法並沒有實際覆蓋超類中的方法,則編譯器會發

出錯誤警告。下面的例子是實現類Child實現了介面Person的方法   使用了@Override。

package com.demo.annotation;

/**
 * 測試@Override註解
 * @author Administrator
 * @date 2016年12月9日
 */
public interface Person {
	
	public String name();
	
	public int age();
	
	public void sing();

}

class Child implements Person {

	@Override
	public String name() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int age() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void sing() {
		// TODO Auto-generated method stub
	    
	}
	
}

 @Deprecated

        它的作用是對不應該再使用的方法添加註解,當程式設計人員使用這些方法時,將會在編譯時顯示提示資訊,它與

javadoc裡的@deprecated標記有相同的功能,準確的說,它還不如javadoc @deprecated,因為它不支援引數,

使用@Deprecated的示例程式碼示例如下:   

package com.demo.annotation;


/**
 * 測試@Deprecated註解
 * @author Administrator
 * @date 2016年12月9日
 */
public interface Person {

public String name();

public int age();

@Deprecated
public void sing();

}

class Child implements Person {

@Override
public String name() {
// TODO Auto-generated method stub
return null;
}

@Override
public int age() {
// TODO Auto-generated method stub
return 0;
}

@Override
public void sing() {
// TODO Auto-generated method stub

}

}

class Test {
@SuppressWarnings("deprecation")
public void sing(){
Person person = new Child();
person.sing();//sing()方法已經過時
}

}

        Person介面使用了@Deprecated修飾了一個已經過時的方法,此時強行呼叫超類繼承來的方法就會有個程式碼過

時的橫線,並且報一個程式碼過時的警告,但不會影響正常使用。如果要去掉警告可以使用

 @suppressWarings("deprecation")  來忽略這個警告。

       @SuppressWarnings

       其引數有:
       deprecation:使用了過時的類或方法時的警告

       unchecked:執行了未檢查的轉換時的警告

       fallthrough:當 switch 程式塊直接通往下一種情況而沒有 break 時的警告

       path:在類路徑、原始檔路徑等中有不存在的路徑時的警告

       serial:當在可序列化的類上缺少serialVersionUID 定義時的警告

       finally :任何 finally 子句不能正常完成時的警告

       all:關於以上所有情況的警告

       這裡的演示在上面已經出現過。

       

       常見的第三方註解

       

       在這裡只是簡單的演示一下,在學習框架的過程中再詳細說明。

       按照過去的方式就是寫一個配置檔案:

       

       使用註解方式不再使用配置檔案:

       

Java註解的分類

       按照執行機制分類

       原始碼註解:註解只在原始碼中存在,編譯成.class檔案就不存在了;

       編譯時註解:註解在原始碼和.class檔案中都存在,比如@SuppressWarnings、@Override和@Deprecated只在

編譯時刻起作用;

       執行時註解:原始碼、編譯後以及執行時都存在的註解,在執行階段還起作用,甚至會影響執行邏輯的註解,比如

spring框架的@Autowired註解。

       按照來源分類

       來自JDK的註解:@SuppressWarnings、@Override和@Deprecated

       來自第三方的註解:比如spring框架的@Autowired註解

       自定義註解:我們自己定義的註解。

       元註解

       註解的註解:自定義註解中比較常見。

       Java自定義註解

       先來看一個模板:

       

        自定義註解語法要求

       1)使用@Interface關鍵字定義註解;

       2)成員以無參無異常方式宣告;

       3)可以用default為成員指定一個預設值;

       4)成員型別是受限的,合法的型別包括原始型別及String、Class、Annotation、Enumeration;

       5)如果註解只有一個成員,則成員必須取名為value(),在使用時可以忽略成員名和賦值號(=);

       6)註解類可以沒有成員,沒有成員的註解稱為標識註解;

        註解的註解(元註解)

       @Target({ElementType.METHOD,ElementType.TYPE})

       CONSTRUCTOR:構造方法宣告
       FIELD:欄位宣告
       LOACL_VARIABLE:區域性變數宣告
       METHOD:方法宣告
       PACKAGE:包宣告
       PARAMETER:引數宣告
       TYPE:類或者介面宣告

       @Retention(RetentionPolicy.RUNTIME)

       SOURCE:只在原始碼顯示,編譯時會丟棄

       CLASS:編譯時會記錄到class中,執行時忽略

       RUNTIME:執行時存在,可以通過反射讀取

       @Inherited

       允許子類繼承

       @Documented

       生成javadoc時會包含註解

       使用自定義註解

       使用註解的語法:@<註解名>(<成員名1>=<成員值1>,<成員名2>=<成員值2>,...)

       一個簡單的例子:

       定義@Description註解:

package com.demo.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;

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
	
	String desc();
	
	String author();
	
	int age() default 18;
	
}

       使用註解:

package com.demo.annotation;

@Description(desc="I am class annotation",author="Mooc boy",age=18)
public class AnnotationTest {
	@Description(desc="I am method annotation",author="Mooc boy",age=18)
	public String eyeColor(){
		return "red";
	}
}

       @Description註解在eyeColor()方法上使用,可以在類或者介面上使用

       解析註解

       概念:通過反射獲取類,函式或成員上的執行時註解資訊,從而實現動態控制控制程式執行的邏輯。

       對於@RetentionRetention表示作用範圍,我們知道:SOURCE表示只在原始碼顯示,編譯時會丟棄;

CLASS表示編譯時會記錄到class中,執行時忽略;RUNTIME表示執行時存在,可以通過反射讀取。前兩個都不會顯

示結果,只有最後一個會顯示結果。

       一個例子:

package com.demo.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * 解析註解
 * @author Administrator
 * @date 2016年12月9日
 */
public class Demo {
	public static void main(String[] args) {
		//1.使用類載入器載入類
		try {
			Class< ?> clazz = Class.forName("com.demo.annotation.AnnotationTest");
		    //2.找到類上面的註解
			boolean flag1 = clazz.isAnnotationPresent(Description.class);
			if(flag1){
				//3.拿到註解例項
				Description description1 = (Description)clazz.getAnnotation(Description.class);
				System.out.println(description1.desc());
				System.out.println(description1.author());
				System.out.println(description1.age());
			}
			
			//4.解析找到方法上的註解
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				boolean flag2 = method.isAnnotationPresent(Description.class);
				if(flag2){
					//5.拿到註解例項
					Description description2 = (Description)method.getAnnotation(Description.class);
					System.out.println(description2.desc());
					System.out.println(description2.author());
					System.out.println(description2.age());
				}
			}
			
			//另外一種解析方法
			for (Method method : methods) {
				Annotation[]  annotations = method.getAnnotations();
				for (Annotation annotation : annotations) {
					if(annotation instanceof Description){
						Description description3 = (Description)annotation;
						System.out.println(description3.desc());
						System.out.println(description3.author());
						System.out.println(description3.age());
					}
				}
			}
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}

}

       執行結果:

       

       關於@Inherited元註解

       父子類繼承註解這塊分兩種情況,一個是註解定義了@Inherited,一個是沒定義。在每種情況中又分類上的注

解,子類實現父類抽象方法,繼承了父類方法,覆蓋了父類方法這四種情況,具體繼承規則如下:

       編寫自定義註解時未寫@Inherited的執行結果:    

       子類的類上能否繼承到父類的類上的註解?    否        

       子類方法,實現了父類上的抽象方法,這個方法能否繼承到註解?    否        

       子類方法,繼承了父類上的方法,這個方法能否繼承到註解?    能        

       子類方法,覆蓋了父類上的方法,這個方法能否繼承到註解?    否        

       編寫自定義註解時寫了@Inherited的執行結果:

       子類的類上能否繼承到父類的類上的註解?     能    

       子類方法,實現了父類上的抽象方法,這個方法能否繼承到註解?      否  

       子類方法,繼承了父類上的方法,這個方法能否繼承到註解?      能 

       子類方法,覆蓋了父類上的方法,這個方法能否繼承到註解?      否   

        僅供參考!