注解的定义
注解通过 @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 {
}
注
:
在
注
解
中
定
义
属
性
时
它
的
类
型
必
须
是
8
种
基
本
数
据
类
型
外
加
类
、
接
口
、
注
解
及
它
们
的
数
组
\color{red}{注: 在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组}
注:在注解中定义属性时它的类型必须是8种基本数据类型外加类、接口、注解及它们的数组
可以给属性设置默认值,默认值用 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 后面的括号里面进行赋值了
如
果
一
个
注
解
内
仅
仅
只
有
一
个
名
字
为
v
a
l
u
e
的
属
性
时
,
应
用
这
个
注
解
时
可
以
直
接
接
属
性
值
填
写
到
括
号
内
\color{red}{如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内}
如果一个注解内仅仅只有一个名字为value的属性时,应用这个注解时可以直接接属性值填写到括号内
如:
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 函数式接口注解
函 数 式 接 口 ( F u n c t i o n a l I n t e r f a c e ) 就 是 一 个 具 有 一 个 方 法 的 普 通 接 口 , 函 数 式 接 口 可 以 很 容 易 转 换 为 L a m b d a 表 达 式 \color{red}{函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口, 函数式接口可以很容易转换为 Lambda 表达式} 函数式接口(FunctionalInterface)就是一个具有一个方法的普通接口,函数式接口可以很容易转换为Lambda表达式
注解与反射
获取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.