注解

Java注解是在JDK1.5 之后出现的新特性,用来说明程序的,注解的主要作用体现在以下几个方面:

  1. 编译检查,例如 @Override
  2. 编写文档,java doc 会根据注解生成对应的文档
  3. 代码分析,通过注解对代码进行分析[利用反射机制]

JDK 中有一些常用的内置注解,例如:

  1. Override:检查被该注解修饰的方法是否是重写父类的方法
  2. Deprecatedd:被该注解标注的内容已过时
  3. SuppressWarnning: 压制警告,传入参数all表示压制所有警告

自定义注解

JDK中虽然内置了大量注解,但是它也允许我们自定义注解,这样就为程序编写带来了很大的便利,像有些框架就大量使用注解。

java注解本质上是一个继承了 java.lang.annotation.Annotation 接口的一个接口,但是如果只是简单的使用关键字 interface来定义接口,仍然不是注解,仅仅是一个普通的接口,在定义注解时需要使用关键字 @interface, 该关键字会默认继承 Annotation 接口,并将定义的接口作为注解使用

注解中可以定义方法,这些方法的返回值只能是基本类型、String、枚举类型、注解以及这些类型的数组,我们称这些方法叫做属性。

在使用注解时需要注意以下几个事情

  1. 必须给注解的属性赋值,如果不想赋值可以使用default来设置默认值
  2. 如果属性列表中只有一个名为value的属性,那么在赋值时可以不用指定属性名称
  3. 多个属性值之间使用逗号隔开
  4. 数组属性的赋值使用 {}, 而当数组属性中只有一个值时, {} 可以省略不写

元注解

元注解是用来描述注解的注解,Java中提供的元注解有下列几个

Target

描述注解能够作用的位置,即哪些Java代码元素能够使用该注解,注解的源代码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}

这个注解只有一个value属性,属性需要传入一个 ElementType枚举类型的数组,该枚举类型可以取下列几个值

ElementType 含义
TYPE 接口、类(包括注解)、枚举类型上使用
FIELD 字段声明(包括枚举常量)
METHOD 方法
PARAMETER 参数声明
CONSTRUCTOR 构造函数
LOCAL_VARIABLE 局部变量声明
ANNOTATION_TYPE 注解类型声明
PACKAGE 包声明
Retention

表示该注解类型的注解保留的时长,主要有3个阶段: 源码阶段,类对象阶段,运行阶段;源码阶段是只只存在与源代码中,类对象阶段是指被编译进 .class 文件中,类对象阶段是指执行时被加载到内存.则默认保留策略为RetentionPolicy.CLASS。

它的源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
Documented

表示拥有该注解的元素可通过javadoc此类的工具进行文档化。源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
Inherited

表示该注解类型被自动继承

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

内置注解解读

下面通过几个JDK内置注解的解读来说明注解相关使用

Override
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

该注解用于编译时检查,被该注解注释的方法是否是重写父类的方法。

从源码上看,它只能在方法上使用,并且它仅仅存在于源码阶段不会被编译进 .class 文件中

Deprecatedd
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

用于告知编译器,某一程序元素(例如类、方法、属性等等)不建议使用

从源码上看,几乎所有的Java程序元素都可以使用它,而且会被加载到内存中

SuppressWarnning
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}

告知编译器忽略特定类型的警告

它需要传入一个字符串的数组,取值如下:

参数 含义
deprecation 使用了过时的类或方法时的警告
unchecked 执行了未检查的转换时的警告
fallthrough 当Switch程序块进入进入下一个case而没有Break时的警告
path 在类路径、源文件路径等有不存在路径时的警告
serial 当可序列化的类缺少serialVersionUID定义时的警告
finally 任意finally子句不能正常完成时的警告
all 以上所有情况的警告

在程序中解析注解

一般通过反射技术来解析自定义注解,要通过反射技术来识别注解,前提条件就是注解要在内存中被加载也就是要使它的范围为 RUNTIME;

JDK提供了以下常用API方便我们使用

返回值 方法 解释
T getAnnotation(Class annotationClass) 当存在该元素的指定类型注解,则返回相应注释,否则返回null
Annotation[] getAnnotations() 返回此元素上存在的所有注解
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注解。
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 当存在该元素的指定类型注解,则返回true,否则返回false

实战

下面使用一个完整的例子来说明自定义注解以及在程序中使用注解的例子,现在来模仿JUnit 定义一个MyTest的注解,只要被这个注解修饰的方法将来都会被自动执行

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

首先定义一个注解,后续来执行用这个注解修饰了的所有方法,通过Target来修饰标明注解只能用于方法上,通过Retention修饰标明注解会被保留到运行期

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; public class Test {
@MyTest
public void test1(){
System.out.println("this is test1");
} @MyTest
public void test2(){
System.out.println("this is test2");
} public static void main(String[] args) {
Method[] methods = Test.class.getMethods();
for (Method method:methods){
if (method.isAnnotationPresent(MyTest.class)){
try {
method.invoke(new Test());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}

在测试类中定义了两个测试函数都使用 @MyTest 修饰,在主方法中,首先通过反射机制获取该类中所有方法,然后调用方法的 isAnnotationPresent 函数判断该方法是否被 @Test修饰,如果是则执行该方法。这样以后即使再添加方法,只要被 @MyTest 修饰就会被调用。

Junit框架

在软件开发中为了保证软件质量单元测试是必不可少的一个环节,Java中提供了Junit 测试框架来进行单元测试

一般一个Java项目每一个类都会对应一个test类用来做单元测试,例如有一个Person类,为了测试Person类会定义一个PersonTest类来测试所有代码

JUnit 中定义了一些注解来方便我们编写单元测试

  1. @Test:测试方法,被该注解修饰的方法就是一个测试方法
  2. @Before:在测试方法被执行前会执行该注解修饰的方法
  3. @After:在测试方法被执行后会执行该注解修饰的方法

除了注解JUnit定义了一些断言函数来实现自动化测试,常用的有如下几个:

  1. void assertEquals(boolean expected, boolean actual):检查两个变量或者等式是否平衡
  2. void assertTrue(boolean expected, boolean actual):检查条件为真
  3. void assertFalse(boolean condition):检查条件为假
  4. void assertNotNull(Object object):检查对象不为空
  5. void assertNull(Object object):检查对象为空
  6. void assertSame(boolean condition):assertSame() 方法检查两个相关对象是否指向同一个对象
  7. void assertNotSame(boolean condition):assertNotSame() 方法检查两个相关对象是否不指向同一个对象
  8. void assertArrayEquals(expectedArray, resultArray):assertArrayEquals() 方法检查两个数组是否相等

这些函数在断言失败后会抛出异常,后续只要查看异常就可以哪些测试没有通过

假设先定义一个计算器类,来进行两个数的算数运算

public class Calc {
public int add(int a, int b){
return a + b;
} public int sub(int a, int b){
return a - b;
} public int mul(int a, int b){
return a * b;
} public float div(int a, int b){
return a / b;
}
}

为了测试这些方法是否正确,我们来定义一个测试类

import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class CalcTest {
@Test
public void addTest(){
int result = new Calc().add(1,2);
assertEquals(result, 3);
} @Test
public void subTest(){
int result = new Calc().sub(1,2);
assertEquals(result, -1);
} @Test
public void mulTest(){
int result = new Calc().mul(1,2);
assertEquals(result, 2);
} @Test
public void divTest(){
float result = new Calc().div(1,2);
assertEquals(result, 0.5, 0.001); //会报异常
}
}

经过测试发现,最后一个divTest方法 会报异常,实际值是0,因为我们使用 / 来计算两个int时只会保留整数位,也就是得到的是0,与预期的0.5不匹配,因此会报异常


Java 注解与单元测试的更多相关文章

  1. Java 工具 JUnit单元测试

    Java 工具 JUnit单元测试 @author ixenos 1.1.   JUnit单元测试框架的基本使用 一.搭建环境: 导入junit.jar包(junit4) 二.写测试类: 0,一般一个 ...

  2. Java Spring Boot VS .NetCore (八) Java 注解 vs .NetCore Attribute

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  3. Java 注解指导手册 – 终极向导

    原文链接 原文作者:Dani Buiza 译者:Toien Liu  校对:深海 编者的话:注解是java的一个主要特性且每个java开发者都应该知道如何使用它. 我们已经在Java Code Gee ...

  4. Java注解处理器

    Java注解处理器 2015/03/03 | 分类: 基础技术 | 0 条评论 | 标签: 注解 分享到:1 译文出处: race604.com   原文出处:Hannes Dorfmann Java ...

  5. java注解总结(1)

    1.什么是注解 注解,主要提供一种机制,这种机制允许程序员在编写代码的同时可以直接编写元数据. 2.介绍 何为注解?--->元数据:描述数据自身的数据. 注解就是代码的元数据,他们包含了代码自身 ...

  6. java注解使用总结

    2005年,sun公司推出了jdk1.5,同时推出的注解功能吸引了很多人的目光,使用注解编写代码,能够减轻java程序员繁琐配置的痛苦. 使用注解可以编写出更加易于维护,bug更少的代码. 注解是什么 ...

  7. Java 注解指导手册(下)

    9. 自定义注解   正如我们之前多次提及的,可以定义和实现自定义注解.本章我们即将探讨. 首先,定义一个注解:   public @interface CustomAnnotationClass   ...

  8. Java 注解指导手册(上)

      编者的话:注解是java的一个主要特性且每个java开发者都应该知道如何使用它.   我们已经在Java Code Geeks提供了丰富的教程, 如Creating Your Own Java A ...

  9. 【Java】Junit单元测试

    什么是单元测试? 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证. 对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Ja ...

随机推荐

  1. 17个你必须牢记的Win10快捷键

    电脑初学者掌握了盲打技术,可以提高录入速度:游戏玩家掌握了快捷键,可以在瞬息百变的对战中提高生存的机会:而Windows玩家掌握了快捷键,不但可以提高电脑操作速度,更能享受到初级玩家望着你那仰慕的眼神 ...

  2. BZOJ 1008 越狱题解

    其实这题很水,显然n个房间有m种宗教,总共有n^m种情况, 我们再考虑不合法的情况,显然第一个房间有m种情况,而后一种只有m-1种情况(因为不能相同) 所以不合法的情况有(m-1)^(n-1)*m种情 ...

  3. vue vscode属性标签不换行

    "vetur.format.defaultFormatterOptions": { "js-beautify-html": { "wrap_attri ...

  4. 阿里云OSS同城冗余存储技术解析

    一.背景 近年来,面对数字化转型带来的挑战,越来越多的企业开始将关键业务系统上云,也有更多的业务创新在云上,帮助企业实现业务增长,这些数据已经成为企业最重要的资产.资源.对于企业来说,如何确保宝贵的数 ...

  5. 《spring boot》8.2章学习时无法正常启动,报“ORA-00942: 表或视图不存在 ”

    在学习<spring boot>一书的过程中,由于原书作者难免有一些遗漏的的地方,或者系统.软件版本不一致.框架更新等各种因素,完全安装书中源码页不能实现项目的正常启动 在8.2章节,演示 ...

  6. c++ 模板和traits

    #define TEST(ITEMNAME) AddItem(ITEMNAME, #ITEMNAME); template <typename T> void AddItem(T& ...

  7. oracle函数 chartorowid(c1)

    [功能]转换varchar2类型为rowid值 [参数]c1,字符串,长度为18的字符串,字符串必须符合rowid格式 [返回]返回rowid值 [示例] SELECT chartorowid('AA ...

  8. 从DataTable中删除不被控件支持的字段类型

    DataTable dt = DB.GetDataTable(sql);                        //从dt中删除不被控件支持的字段类型            for (int ...

  9. oracle trunc(d1[,c1])

    [功能]:返回日期d1所在期间(参数c1)的第一天日期 [参数]:d1日期型,c1为字符型(参数),c1默认为j(即当前日期) [参数表]:c1对应的参数表: 最近0点日期: 取消参数c1或j 最近的 ...

  10. hdu 1532 Drainage Ditches(最大流模板题)

    Drainage Ditches Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...