1. 程式人生 > >java編程思想之註解

java編程思想之註解

ecs 參數 下載 定義 循環調用 download wpa dna instance

註解 (元數據) 為我們在代碼中添加信息提供了一種形式化的方法,使我們可以在稍後的某個時刻非常方便的使用這些數據。

註解在一定程度上是在把元數據與源代碼文件結合在一起,而不是保存在外部文檔中。註解是眾多引入 javaSE5 中的重要語言變化之一。他們可以提供用來完整地描述程序所需的信息,而這些信息是無法用 Java 來表達的。註解可以用來生成描述文件,甚至或是新的類定義,並且有助於編寫減輕樣板代碼的負擔。通過使用註解,我們可以將這些元數據保存在 Java 源代碼中,並利用 annotation API 為自己的註解構造處理工具,同時,註解的優點還包括:更加幹凈易讀的代碼以及編譯期類型檢查等。雖然 Java SE5 預先定義了一些元數據,但一般來說,主要還是需要程序員自己添加新的註解,並且按照自己的方式使用它們。

註解的語法比較簡單,除了 @ 符號的使用外,它基本與 Java 固有的語法一樣。Java SE5 內置了三種,定義在 java.lang 中的註解:

  • @Override ,表示當前的方法定義將覆蓋超類中的方法。
  • @Deprecated ,如果程序員使用了註解為它的元素,那麽編譯器會發出警告信息。
  • @SuppressWarnings ,關閉不當的編譯器警告信息。Java SE5 之前的版本也可以使用這個註解,不過會被忽略不起作用。

每當你創建了描述性質的類或接口時,一旦其中包含了重復性的工作,那就可以考慮使用註解來簡化與自動化該過程。註解是在實際的源代碼級別保存所有的信息,而不是某種註釋性的文字,這使得代碼更整潔,且便於維護。通過使用擴展的 annotation API,或外部的字節碼工具類庫,程序員擁有對源代碼以及字節碼強大的檢查與操作能力。

基本語法

下面示例中,使用 @Test 對 TestExecute() 方法進行註解。這個註解本身並不做任何事情,但是編譯器要確保在其構造路徑上必須有 @Test 註解的定義。

public @interface Test {

}

public class Testble {
	public void execute() {
		System.out.println("Executing..");
	}

	@Test
	void testExecute(){
		execute();
	}
}

被註解的方法與其他的方法沒有區別。 @Test 可以與任何修飾符共同作用域方法。

定義註解

上面的例子註解的定義我們已經看到了。註解的定義看起來很像接口的定義。事實上與任何 Java 文件一樣,註解也會被編譯為 class 文件。除了 @

符號以外, @Test 的定義很像一個空的接口。定義註解時會需要一些元註解,如 @Target@Retention@Target 用來定義你的註解將應用於什麽地方。 @Deprecated 用來定義應該用於哪一個級別可用,在源代碼中、類文件中或者運行時。

在註解中一般都會包含某些元素用以表示某些值。當分析出來註解時,程序和工具可以利用這些值。註解的元素看起來就像接口的方法,唯一的區別是你可以為他指定默認值。沒有元素的註解被稱為標記註解。

下面是一個簡單的註解,它可以跟蹤一個項目中的用例。程序員可以在該方法上添加註解,我們就可以計算有多少已經實現了該用例。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
	public int id();
	public String description() default "沒有描述";
}

註意:id 和 description 類似方法的定義。description 元素有一個 default 值,如果在註解某個方法時沒有給出 description 的值,則就會使用這個默認值。

下面的三個方法被註解:

public class PasswordUtils {
	@UseCase(id =47,description = "password 哈哈哈防止破解")
	public boolean validatePassword(String password) {
		return (password.matches("\\w*\\d\\w*"));
	}

	@UseCase(id = 48)
	public String encryptPassword(String password) {
		return new StringBuilder(password).reverse().toString();
	}

	@UseCase(id = 49,description = "是否包含在這個密碼庫中")
	public boolean checkForNewPassword(List<String> prevPassword,String password) {
		return !prevPassword.contains(password);
	}
}

註解的元素在使用時是名值對的形式放入註解的括號內。

元註解

Java 目前只內置了三種標準註解,以及四種元註解。元註解就是註解的註解:

@Target 表示註解可以用在什麽地方。ElementType 的參數包括:
CONSTRUCTOR:構造器的聲明
FIELD:域聲明
LOCAL_VARIABLE:局部變量聲明
METHOD:方法聲明
PACKAGE:包聲明
PARAMETER:參數聲明
TYPE:類、接口或enum聲明
@Retention 表示需要在什麽級別保存註解信息。可選的RententionPolicy參數:
SOURCE:註解將被編譯器丟失
CLASS:註解在class文件中可用,但會被 vm 丟失
RUNTIME:vm 在運行期也保留註解,因此可以通過反射機制讀取註解的信息。
@Documented 將此註解包含在 javaDoc 中
@Inherited 允許子類繼承父類中的註解

大多數時候我麽都是編寫字節的註解,並編寫自己的處理器處理他們。

編寫註解處理器

如果沒有用來讀取註解的工具,那麽註解就不會這有用。使用註解很重要的就是創建和使用註解處理器。Java SE5 擴展了反射機制的 API,方便我們構造這種工具。同時還提供了一個外部工具 apt 幫助我們解析帶有註解的 Java 源代碼。

下面我們就用反射來做一個簡單的註解處理器。我們用它來讀取上面的 PasswordUtils 類。

public class UseCaseTracker {

	public static void trackUseCase(List<Integer> useCase,Class<?> cl) {
		for (Method method : cl.getDeclaredMethods()) {
			UseCase uCase = method.getAnnotation(UseCase.class);
			if (uCase != null) {
				System.out.println("方法上的註解信息:"+uCase.id()+"  "+uCase.description());
			}
		}

		for (Integer integer : useCase) {
			System.out.println("參數:"+integer);
		}
	}

	public static void main(String[] args) {
		List<Integer> uList = new ArrayList<>();
		Collections.addAll(uList, 47,48,49,50);
		trackUseCase(uList, PasswordUtils.class);
	}

}

測試結果:

方法上的註解信息:49  是否包含在這個密碼庫中
方法上的註解信息:48  沒有描述
方法上的註解信息:47  password 哈哈哈防止破解
參數:47
參數:48
參數:49
參數:50

上面用到了兩個反射的方法:getDeclaredMethods() 和 getAnnotation(),getAnnotation() 方法返回指定類型的註解對象,在這裏使用 UseCse。如果被註解的方法上沒有改類型的註解,則返回 null 值。然後我們從返回的 UseCase 對象中提取元素的值。

註解元素