註解的定義
註解通過 @interface 關鍵字進行定義。
public @interface MyAnnotation {
}
註解的應用
上面創建了一個註解,那麼註解的的使用方法是什麼呢。
@MyAnnotation
public class MyTest {
...
}
元註解
元註解是可以註解到註解上的註解,或者說元註解是一種基本註解,但是它能夠應用到其它的註解上面。
-
@Retention:
取值:- RetentionPolicy.SOURCE 註解只在源碼階段保留,在編譯器進行編譯時它將被丟棄忽視。
- RetentionPolicy.CLASS 註解只被保留到編譯進行的時候,它並不會被加載到 JVM 中。
- RetentionPolicy.RUNTIME 註解可以保留到程序運行的時候,它會被加載進入到 JVM 中,所以在程序運行時可以獲取到它們。
例子:
@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { }
-
@Documented: 能夠將註解中的元素包含到 Javadoc 中去
-
@Target: 指定了註解運用的地方。
@Target 有下面的取值- ElementType.ANNOTATION_TYPE 可以給一個註解進行註解
- ElementType.CONSTRUCTOR 可以給構造方法進行註解
- ElementType.FIELD 可以給屬性進行註解
- ElementType.LOCAL_VARIABLE 可以給局部變量進行註解
- ElementType.METHOD 可以給方法進行註解
- ElementType.PACKAGE 可以給一個包進行註解
- ElementType.PARAMETER 可以給一個方法內的參數進行註解
- ElementType.TYPE 可以給一個類型進行註解,比如類、接口、枚舉
-
@Inherited: 如果一個超類被 @Inherited 註解過的註解進行註解的話,那麼如果它的子類沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解
例:@Inherited @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { } @MyAnnotation public class ClassA { } public class ClassB extends ClassA { }
註解 MyAnnotation 被 @Inherited 修飾,之後類 ClassA 被 MyAnnotation 註解,類 ClassB 繼承 ClassA ,類 ClassB 也擁有 MyAnnotation 這個註解。
-
@Repeatable: 註解可多次應用,可重複
註解的屬性
- 註解的屬性也叫做成員變量。
- 註解只有成員變量,沒有方法。
- 註解的成員變量在註解的定義中以“無形參的方法”形式來聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
int id();
String msg();
}
使用的時候可對其賦值:
@TestAnnotation(id=1,msg="hello annotation")
public class Test {
}
可以給屬性設置默認值,默認值用 default 關鍵值指定。
例:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
public int id() default -1;
public String msg() default "Hello";
}
//有了默認值,可以這樣使用
@MyAnnotation ()
public class Test {}
//所以無需要再在 @MyAnnotation 後面的括號裏面進行賦值了
如:
public @interface MyAnnotation {
public String value();
}
//可這樣使用
@MyAnnotation ("hi")
int a;
//和
@MyAnnotation(value="hi")
int a;
的效果是一樣的
註解沒有任何屬性
public @interface Perform {}
//使用時括號可以省略
@Perform
public void testMethod(){}
Java 預置的註解
- @Deprecated 用來標記過時的元素
- @Override 子類要重載父類的方法
- @SuppressWarnings 阻止警告
- @SafeVarargs 參數安全類型註解, 提醒開發者不要用參數做一些不安全的操作,它的存在會阻止編譯器產生 unchecked 這樣的警告
- @FunctionalInterface 函數式接口註解
註解與反射
獲取Class上註解的方法:
- isAnnotationPresent() : 判斷類的對象是否應用了某個註解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
- getAnnotation() :方法來獲取 Annotation 對象
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
- getAnnotations() :返回註解到這個元素上的所有註解 Annotation 對象
public Annotation[] getAnnotations() {}
例子
例子一:
@MyAnnotation()
public class Test {
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
System.out.println("id:"+testAnnotation.id());
System.out.println("msg:"+testAnnotation.msg());
}
}
}
例子二,可檢索出屬性方法上的註解:
@MyAnnotation(msg="hello")
public class Test {
@Check(value="hi")
int a;
@Perform
public void testMethod(){}
@SuppressWarnings("deprecation")
public void test1(){
Hero hero = new Hero();
hero.say();
hero.speak();
}
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
//獲取類的註解
System.out.println("id:"+testAnnotation.id());
System.out.println("msg:"+testAnnotation.msg());
}
try {
//取得類的指定屬性
Field a = Test.class.getDeclaredField("a");
a.setAccessible(true);
//獲取一個成員變量上的註解
Check check = a.getAnnotation(Check.class);
if ( check != null ) {
System.out.println("check value:"+check.value());
}
Method testMethod = Test.class.getDeclaredMethod("testMethod");
if ( testMethod != null ) {
// 獲取方法中的註解
Annotation[] ans = testMethod.getAnnotations();
for( int i = 0;i < ans.length;i++) {
System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
}
}
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
使用場景
一個測試代碼BUG的例子
Jiecha.java
@Retention(RetentionPolicy.RUNTIME)
public @interface Jiecha {
}
被測試的類
NoBug.java
public class NoBug {
@Jiecha
public void suanShu(){
System.out.println("1234567890");
}
@Jiecha
public void jiafa(){
System.out.println("1+1="+1+1);
}
@Jiecha
public void jiefa(){
System.out.println("1-1="+(1-1));
}
@Jiecha
public void chengfa(){
System.out.println("3 x 5="+ 3*5);
}
@Jiecha
public void chufa(){
System.out.println("6 / 0="+ 6 / 0);
}
public void ziwojieshao(){
System.out.println("我寫的程序沒有 bug!");
}
}
測試類
TestTool.java
public class TestTool {
public static void main(String[] args) {
// TODO Auto-generated method stub
NoBug testobj = new NoBug();
Class clazz = testobj.getClass();
Method[] method = clazz.getDeclaredMethods();
//用來記錄測試產生的 log 信息
StringBuilder log = new StringBuilder();
// 記錄異常的次數
int errornum = 0;
for ( Method m: method ) {
// 只有被 @Jiecha 標註過的方法才進行測試
if ( m.isAnnotationPresent( Jiecha.class )) {
try {
m.setAccessible(true);
m.invoke(testobj, null);
} catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
errornum++;
log.append(m.getName());
log.append(" ");
log.append("has error:");
log.append("\n\r caused by ");
//記錄測試過程中,發生的異常的名稱
log.append(e.getCause().getClass().getSimpleName());
log.append("\n\r");
//記錄測試過程中,發生的異常的具體信息
log.append(e.getCause().getMessage());
log.append("\n\r");
}
}
}
log.append(clazz.getSimpleName());
log.append(" has ");
log.append(errornum);
log.append(" error.");
// 生成測試報告
System.out.println(log.toString());
}
}
測試的結果:
1234567890
1+1=11
1-1=0
3 x 5=15
chufa has error:
caused by ArithmeticException
/ by zero
NoBug has 1 error.