Java 1.5之前,一般使用命名模式表明有些程序元素需要通过某种工具或者框架进行特殊处理。例如,JUnit测试框架原本要求用户一定要用test作为测试方法名称的开头。

命名模式的缺点:

  • 文字拼写错误导致失败,测试方法没有执行,也没有报错
  • 无法确保它们只用于相应的程序元素上,如希望一个类的所有方法被测试,把类命名为test开头,但JUnit不支持类级的测试,只在test开头的方法中生效
  • 没有提供将参数值与程序元素关联起来的好方法。

注解能解决命名模式存在的问题,下面定义一个注解类型指定简单的测试,它们自动运行,并在抛出异常时失败
(注意,下面的Test注解是自定义的,不是JUnit的实现)

import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {}

@Retention注解有三种
1.RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
2.RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
3.RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.

@Target(ElementType.METHOD)表明只有在方法声明中Test注解才是合法的

下面的Sample类使用Test注解,如果拼错Test或者将Test注解应用到除方法外的其他地方,
编译不会通过

public class Sample {@Test public static void m1() {}public static void m2() {}@Test public static void m3() {throw new RuntimeException("Boom");}public static void m4() {}@Test public void m5() {}public static void m6() {}@Test public static void m7() {throw new RuntimeException("Crash");}public static void m8() {}
}

下面是测试运行类:

public class RunTests {public static void main(String[] args) throws ClassNotFoundException {//测试的个数int tests = 0;//测试通过的个数int passed = 0;Class testClass = Class.forName("ex_35.Sample");for(Method m : testClass.getDeclaredMethods()) {//如果方法上存在@Test注解if(m.isAnnotationPresent(Test.class)) {tests++;try {m.invoke(null);passed++;//如果测试方法抛出异常} catch(InvocationTargetException wrappedExc) {Throwable exc = wrappedExc.getCause();System.out.println(m + " failed: " + exc);//如果方法是无效的Test用法,} catch(Exception e) {System.out.println("INVALID @Test: " + m);}}}System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);}}

就完成了对@Test注解方法的测试,结果如下:

public static void ex_35.Sample.m3() failed: java.lang.RuntimeException: Boom
INVALID @Test: public void ex_35.Sample.m5()
public static void ex_35.Sample.m7() failed: java.lang.RuntimeException: Crash
Passed: 1, Failed: 3

针对只有在抛出异常才成功的注解(有参数注解的方法):

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {Class<? extends Exception> value();
}

使用:

public class Sample2 {@ExceptionTest(ArithmeticException.class) public static void m1() {int i = 1/0;}

测试工具方法也很容易写,我们知道方法抛出异常时会进入InvocationTargetException,所以在catch那里判断一下抛出异常的类型就好了。

其他地方与上一个测试类一样:

catch(InvocationTargetException wrappedExc) {Throwable exc = wrappedExc.getCause();//得到注解上的参数Class<? extends Exception> excType = m.getAnnotation(ExceptionTest.class).value();if(excType instanceof(exc)){pass++;}

看懂了测试工作类的写法,其实离Junit的理解也就很近了,后面我会分析分析Junit的源代码。

这里主要介绍注解比命名模式的优点,但是实际上我们写JavaEE用的命名模式很少,几乎都让Spring注解给替代了。如果看到必须要特殊进行命名的类或者方法,甚至是文件,我们都要留个心眼。

Effective Java之注解优于命名模式(三十五)相关推荐

  1. Effective Java之必要时进行保护性拷贝(三十九)

    我们来看一个不可变对象的攻守问题: public class Period{private final Date startTime;private finale Date endTime;publi ...

  2. Effective Java之检查参数的有效性(三十八)

    检查参数的有效性实际上是满足了这一条普遍原则: 应该在发生错误之后尽快检测出错误. 例子: 有个数据库查询的例子,传入一个id,查出一个Student对象,然而返回null,如果没有及时检查这个实例是 ...

  3. Effective Java之用EnumSet代替位域(三十二)

    什么是位域?为什么用到它? 先来看一个例子: public class Test {public static final byte STYLE_BOLD = 1<<0; // 1publ ...

  4. Effective Java之保护性编写readObject方法(七十六)

    readObject方法实际上相当于另一个公有的构造器,与其他构造器一样,它也需要进行参数的有效性检查与保护性拷贝.参考:Effective Java之必要时进行保护性拷贝(三十九) 原因很简单,为了 ...

  5. JAVA之旅(三十五)——完结篇,终于把JAVA写完了,真感概呐!

    JAVA之旅(三十五)--完结篇,终于把JAVA写完了,真感概呐! 这篇博文只是用来水经验的,写这个系列是因为我自己的java本身也不是特别好,所以重温了一下,但是手比较痒于是就写出了这三十多篇博客了 ...

  6. 三十五、深入Java中的泛型(下篇)

    @Author:Runsen @Date:2019年10月26日 17:10:34 作者介绍:Runsen目前大三下学期,专业化学工程与工艺,大学沉迷日语,Python, Java和一系列数据分析软件 ...

  7. 【零基础学Java】—System类(三十五)

    [零基础学Java]-System类(三十五) java.lang.System 类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作 在System类的API文档中,常用的方法有: pub ...

  8. Java学习笔记(三十五)

    在完成对C语言的学习后,我最近开始了对C++和Java的学习,目前跟着视频学习了一些语法,也跟着敲了一些代码,有了一定的掌握程度.现在将跟着视频做的笔记进行整理.本篇博客是整理Java知识点的第三十五 ...

  9. NeHe OpenGL第三十五课:播放AVI

    NeHe OpenGL第三十五课:播放AVI 在OpenGL中播放AVI: 在OpenGL中如何播放AVI呢?利用Windows的API把每一帧作为纹理绑定到OpenGL中,虽然很慢,但它的效果不错. ...

最新文章

  1. linux dmesg信息哪来的,linux中的dmesg命令简介
  2. ffmpeg结构体(二)
  3. 拓展视野学习前端,我推荐这些
  4. (转)Oracle中实现行列转换的方法
  5. echarts中datazoom相关配置
  6. 用python画糖葫芦_python学习记录六
  7. 2019 年,Rust 与 WebAssembly 将让 Web 开发更美好
  8. 【C语言取反运算符】~2是多少?~-5是多少?
  9. 草图大师:SketchUp 2019 for Mac
  10. Java: Hook技术
  11. java 找不到或无法加载主类 test_JAVA报找不到或无法加载主类的错误
  12. 计算机弹奏两只老虎爱跳舞,原神风物之诗琴乐谱
  13. 用Python下载Lofter上“喜欢”的文章和图片
  14. Linux系统盘满了,如何解决。
  15. 【在SpringBoot项目中使用Validation框架检查数据格式-常用的检查注解】
  16. android+微信支付
  17. 什么是NETBIOS?
  18. 2019届阿里巴巴春招前端面试经历
  19. 虹科分享 | 盘点世界杯有趣小知识!带你感受体育赛事可视化的快乐!
  20. JEM software ticket45:Console output error of nQP when LCU level rate control is enabled

热门文章

  1. C++中placement new操作符(经典)
  2. 数据库的同步和复制----sql语句方法
  3. 监视窗口添加 $err,hr 一行来实时现实错误
  4. Golang 常见设计模式之装饰模式
  5. 使用VMware VSphere WebService SDK进行开发 (三)——获取主机(HostSystem)的基本信息
  6. DjangoORM框架
  7. 基于Huffman算法和LZ77算法的文件压缩的改进方向
  8. 开源声码器WORLD在语音合成中的应用
  9. FFmpeg在Intel GPU上的硬件加速与优化
  10. 应用于CDN的GSLB系统