JDK1.5之后,引入了元数据的概念,也就是Annotation(注解),其实它是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。可以帮助程序在编译的时候进行检查
注解的作用有以下三个:
1. 编写文档:通过代码里标识的注解生成文档。
2. 代码分析:通过代码里标识的注解对代码进行分析。
3. 编译检查:通过代码里标识的注解让编译器能实现基本的编译检查。
几种常见的注解:
Markerannotation 标识型的注解,程序见到了这种注解就知道你要干什么,所以会对你下面的内容进行检查
a) @Override 注解表示子类要重写(override)父类的对应方法。
b) @Deprecated 注解表示方法是不建议被使用的。
c) @SuppressWarnings(“unchecked”) 注解表示抑制unchecked类型的警告。 如果你给一个类压制了某种警告,在类的某个方法中压制了另一种警告,则在该方法中会压制这两种警告
自定义注解:@interface 标识
当注解中的属性名为 value 时,在对其赋值时可以不指定属性的名称
而直接写上属性值即可;除了value 以外的其他值都需要使用 name=value这种赋值方式,即明确指定给谁赋值。
使用的时候,直接 @AnnotationTest,
有属性的时候要为属性复制,下面的例子的调用必须这样调用 @AnnotationTest(“aaa”)
// 表示自定义了一个注解
public @interface AnnotationTest {
// 注解中特殊的用法,定义了一个字符串,在外面使用的时候要为value赋值, 也可以设置一个默认值
// 而且value这个名字还有特殊之处,
String value() default "hello";
}
当注解里面有两个属性的时候,要为两个属性都赋值,并且指定所有的属性名字 @AnnotationTest(value1=”aa”, value = “aaa”)
在使用注解的时候,实际上已经自动继承了 java.lang.annotation.Annotation接口,但继承了这个接口,并不意味着就是一个注解,注解的定义必须是通过 @interface 关键字定义的
Annotation 本身是接口而不是注解。可以与Enum 类比,Enum也是继承自Enum类
4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。
1.@Target, 定义自定义的注解可以修饰的是类,或者成员方法,或者成员变量 或者 参数等等, 限定了自定义的注解的修饰权限,指示自定义的注解可以修饰哪些对象。
Target和下面的Retention 有些类似,都需要实现一个枚举类型的形态定义。
// 定义了下面自定义的注解类型只能修饰成员方法 和 类接口枚举类型
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface AnnotationTest {
}
public enum ElementType{
ANNOTATION_TYPE,// 注解
CONSTRUCTOR , // 构造函数
FIELD , // 成员变量
LOCAL_VARIABLE , // 局部变量
METHOD , // 成员方法
PACKAGE , // 包
PARAMETER , // 参数
TYPE , // Class, interface (including annotation type), or enum declaration
TYPE_PARAMETER, // Type parameter declaration
TYPE_USE // Use of a type
}
2.@Retention, 里面有一个属性 value, 类型RetentionPolicy型, 是可以在我们自己自定义的注解中,指示编译器应该如何对待我们自定义的注解 Annotation
在使用Retention形态时,需要提供 java.lang.annnotation.RetentionPolicy的枚举型态,
public enum RetentionPolicy{
SOURCE, // 编译器处理完Annotation信息后就完成任务,不会被编译到.class文件中,只是在源文件中 @SuppressWarnings,@Override的级别就是source
CLASS, // 默认情况下的编译器对注解的处理情况, 编译器会将注解信息留在.class 文件中,但是不会被java的虚拟机读取,仅仅用于在编译程序或者工具程序运行时提供信息
RUNTIME // 编译程序将注解保留在.class文件中,也可以由JVM读取(通过反射的方式读取),以便在程序分析时能够分析注解的情况 @Deprecated的级别就是RUNTIME
}
@Retention(RetentionPolicy.CLASS)
public @interface AnnotationTest {
String value();
}
上面的代码可以说明注解是可以修饰注解的
当指定编译器处理注解Annotation的保留级别是RUNTIME时,注解可以被JVM通过反射的方式读取出来 它的读取方法如下:
需要注意的是: 只有注解中被定义为RUNTIME时,才能被反射所获取到。
@AnnotationTest(value = "aaa", value1 = "bbb")
public class AnnotationUse {
@AnnotationTest(value = "a", value1 = "b")
@Deprecated // 指示这个类是不被建议使用的
@SuppressWarnings("uncheckd")
public void annotation(){
System.out.println("annotation");
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 实现获得annotation上面的注解信息
AnnotationUse annotest = new AnnotationUse();
Class<?> classType = annotest.getClass();
// 获取该类上的所有注解
classType.getAnnotations();
Method method = classType.getMethod("annotation", new Class[]{});
// 获得method上的所有注解, 并打印 ,
// 获得的注解并不包含它的上一层(类上)所写的注解, 虽然上一层类的注解也同样的限制了这个方法
// 同时只能获取 注解中的Retention类型定义为为RUNTIME的注解,
Annotation[] anno = method.getAnnotations();
for (Annotation an: anno){
System.out.println(an.annotationType().getName());
// Annotation.AnnotationTest
//java.lang.Deprecated 结果只有一个注解的名字,
// 这是因为@SuppressWarnings("uncheckd")注解的类型是 SOURCE,
// 它无法被加载到JVM上,因此不能通过反射来得到
}
// 判断在method方法上是否有AnnotationTest这个注解
if (method.isAnnotationPresent(AnnotationTest.class)){
method.invoke(annotest, new Object[]{}); // 输出为 annotation
// 获取注解,并得到注解的内容
AnnotationTest an = method.getAnnotation(AnnotationTest.class);
an.value();
an.value1();
}
}
}
3.@Documented,
定义了一个注解能够通过javadoc 或者其他工具被写入到文件中去
4.@Inherited
加上@Inherited,定义了一个父类的注解,如果被继承,那么子类中也继承了这个注解,
默认情况下, 父类的注解是不能被子类所继承的
JUnit : java实现单元测试的一个框架, 有两种经典的版本
3.8(通过反射实现)
4.x( 反射加上注解实现)
JUnit 山的名言: keep the bar green to keep the code clean (调试的结果是绿色的,代码就是良好的)
视频老师的名言: 如果没有反射,很多框架都不存在了
要使用这个框架,需要先导入jar包,jar包就是许多.class 文件的一个打包
JUnit3.8 里面的测试函数必须以 test开头,否则程序不允许执行,这是因为JUnit是通过反射实现的,获取类名,通过类名获取方法名,在使用方法的invoke方法执行方法,因此你的使用的方法名必须符合他的规则
import junit.framework.TestCase;
public class TestJunit3_8 extends TestCase{
public void testHello(){
System.out.println("helloworld");
}
}
JUnit4.x 通过注解的方式,方法的名字就不必要以test开头, 里面的执行流程大致如下
首选通过反射获得类名,然后通过类名获得方法名,然后获得方法名上的注解,判断该注解上是否有Test注解,如果有,则调用Method上的invoke方法。
所以可以猜测,Test注解的 Target 为ElsemetType.METHOD
Retention 的 RetentionPolicy.RUNTIME
JUnit4 的执行的一般流程:
a) 首先获得待测试类所对应的 Class对象。
b) 然后通过该 Class对象获得当前类中所有 public 方法所对应的 Method 数组。
c) 遍历该Method数组,取得每一个Method对象
d) 调用每个Method 对象的isAnnotationPresent(Test.class)方法,判断该方法是否被 Test 注解所修饰。
e) 如果该方法返回true,那么调用method.invoke()方法去执行该方法,否则不执行。
单元测试不是为了证明你是对的,而是证明你没有错误。
JUnit 使用进行单元测试是比较简单的,困难的是 测试用例的设计
在一本书 Writing Secure Code(编写安全的代码) 中:
要假设: Input is evil。 用户是邪恶的。
import org.junit.Test;
public class TestJunit4_x {
@Test
public void hello(){
System.out.println("hello");
}
}