大数据JavaWeb之java基础巩固----Junit&反射&注解
最近打算从0开始学学大数据,目前的主业是Android开发,但是当年毕业之后其实是搞J2EE的,所以打算没事又来拓展一下后台的技能,扩宽一下自己的知识体系对于自己的未来也能够多一些可能,另外大数据的一些知识其实对于搞Android也是有一些技能补充的,每一项技术并不是单独存在的,或多或少都会有一些交集,接下来会踏踏实实的从0开始一步步来学习它。
Junit:
关于它应该对于搞Java的人再熟悉不过了,不过为了觉得比较扎实,这里再来复习复习。
测试分类:
- 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
也就是只关注输入和输出,中间的流程是看不到的,好像被黑布蒙住了一样。如搞Android测试的那些,大部分全是这种,各种手指点点点就可以了。 - 白盒测试【较高端,对于有追求的测试人员就应该成为这类~~】:需要写代码的。关注程序具体的执行流程。
也就是输入输出的同时,其中中间的转换流程也要关注。
那了解了上面的测试分类之后,对于Junit这种单元测试它是属于哪种呢?它是属于白盒测试的一种,那下面咱们来看一下它的使用:
使用步骤:
1. 定义一个测试类(测试用例)
建议:
测试类名:被测试的类名Test CalculatorTest
包名:xxx.xxx.xx.test
2. 定义测试方法:可以独立运行
建议:
方法名:test测试的方法名 testAdd()
返回值:void
参数列表:空参
3. 给方法加@Test
4. 导入junit依赖环境
5、判定结果:
红色:失败
绿色:成功
一般我们会使用断言操作来处理结果
Assert.assertEquals(期望的结果,运算的结果);
由于比较简单,这里就不实践了。
@Before&@After:
@Before:
修饰的方法会在测试方法之前被自动执行
@After:
修饰的方法会在测试方法执行之后自动被执行
上面的简单使用一下,工具还是使用IntelliJ IDEA:
那如果运行失败了,这俩标注的代码还会运行么?试一下:
反射:框架设计的灵魂
啥叫框架?半成品软件。可以在框架的基础上进行软件开发,简化编码。
啥叫反射?将类的各个组成部分封装为其他对象,这就是反射机制,其好处:
1. 可以在程序运行过程中,操作这些对象。
2. 可以解耦,提高程序的可扩展性。
下面来简单梳理一下整个Java代码在计算机中经历的三个阶段:
获取Class对象的方式:
- Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。
多用于配置文件,将类名定义在配置文件中。读取文件,加载类。下面来看下代码:
先定义一个类:
- 类名.class:通过类名的属性class获取。多用于参数的传递,下面演示一下:
- 对象.getClass():getClass()方法在Object类中定义着。
多用于对象的获取字节码的方式。
下面演示一下:
那下面来将这三种生成的Class对象做下等号比较,看是不是同一个对像:
所以有个如下结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
Class对象功能:
- 获取成员变量们:
1、Field[] getFields() :获取所有public修饰的成员变量
下面来演示一下:下面运行一下:
居然一个都木有打印出来。。因为它只获取public修饰的成员变量,而咱们目前Person类中木有public修饰的成员变量:
所以。。接下来咱们定义一下:
再次运行:
2、Field getField(String name) 获取指定名称的 public修饰的成员变量
那光获取它有啥用呢?肯定得设置和获取值嘛,所以其使用规则:
下面来使用一下:
3、Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符:
4、Field getDeclaredField(String name) 获取指定名称的成员变量
那既然私有的属性也能获取,那是不是私有的属性也能设置和读取呢?下面来试一下:
此时就需要增加一句代码既可:
其实像私有的成员方法、构造方法其要访问也需要忽略掉这个安全检查。
- 获取构造方法们:
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(类<?>... parameterTypes)Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
下面来演示一下:
Constructor<?>[] getDeclaredConstructors()
而获取的构造方法之后,很明显是可以创建对象的嘛,所以其Constructor创建对象的方法如下:
下面来试一下:
那如果不带参数来构造呢?这种空参的方式可以用简化的方式,通过Class.newInstance()方法,如下:
- 获取成员方法们:
Method[] getMethods()
下面来演示一下,先给类中添加几个方法:
Method getMethod(String name, 类<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>... parameterTypes)下面通过反射来调用一下它:
那有了Method如何来执行它呢?如下:
好,下面来用一下:
下面再来定一个带参数的方法:
再来调用一下:
接下来获取所有public修饰的方法:
- 获取全类名:
String getName()
下面来演示一下:
小案例练习:
需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法。
实现:
1. 配置文件
2. 反射
步骤:
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中。
- 在程序中加载读取配置文件。
- 使用反射技术来加载类文件进内存。
- 创建对象。
- 执行方法。
编译运行:
好,如果我们再新建一个类,然后里面定义一个方法,在不修改方法调用代码,只需要改配置既可进行指定方法的调用了,如下:
注解:
概念:说明程序的。给计算机看的。
注释:用文字描述程序的。给程序员看的。
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
概念描述:
- JDK1.5之后的新特性。
- 说明程序的。
- 使用注解:@注解名称。
作用分类:
- 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】。
- 代码分析:通过代码里标识的注解对代码进行分析【使用反射,自定义注解主要是用于它】。
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】。
JDK中预定义的一些注解:
- @Override :检测被该注解标注的方法是否是继承自父类(接口)的。
如果修改方法名,则直接会报错:
也就是它会对安全进行一个检查。 - @Deprecated:该注解标注的内容,表示已过时
这个见得比较多,不多说。 - @SuppressWarnings:压制警告
一般传递参数all @SuppressWarnings("all")
自定义注解:
格式:
元注解
public @interface 注解名称{
属性列表;
}
本质:
注解本质上就是一个接口,该接口默认继承Annotation接口,下面咱们定义一个:
其中元注解先不用定义,为了探究其本质,咱们反编译看一下:
属性:
接口中的抽象方法,它有如下要求:
- 属性的返回值类型有下列取值:
1、基本数据类型
2、String3、枚举
下面咱们先定义一个枚举:然后在注解中定义一个这样的属性:
4、注解
那咱们先定义一个其它注解:5、以上类型的数组
除了以上列的类型,其它返回类型则会报错的,比如咱们新建一个类:然后返回这个类型:
- 定义了属性,在使用时需要给属性赋值:
1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
下面咱们来使用一下咱们自定义的注解,为了方便这里只定义一个方法,其它的暂且先注释掉:
此时报错了:
这是因为还要指定show1这个属性,也就是我们在注解类中定义的方法其实在使用时是相当于一个属性来使用的,所以:
而既然是属性,那么平常咱们在定义注解时给方法就得以属性的形式来命名,这样会比较顺期自然,如下:
再定义一个属性:
另外咱们可以在定义属性时就给其定义默认值,那么咱们在使用注解时就不必一定得要显示给它主动赋值了,比如:
而如果咱们将属性名声明为value,则咱们在使用时就更加简单了,实际开源框架中也大量会用到这个特性,如下:
然而此时的value可以省略掉:
上面的这种写法,有一个前提:“如果只有一个属性需要赋值,并且属性的名称是value”,这点需要特别的注意!!下面再来看一下其它类型的赋值情况,如:
此时就需要这样来使用了:
接下来再来看一下注解类型:
使用:
接下来再看一下数组类型:
使用一下:
其中关于数组还有一个特点:“如果数组中只有一个值,则{}可以省略”,比较简单,这里就不演示了。
元注解:
用于描述注解的注解。咱们需要掌握的注解有如下:
- @Target:描述注解能够作用的位置,其中ElementType取值:
TYPE:可以作用于类上
METHOD:可以作用于方法上
FIELD:可以作用于成员变量上
下面来试一下,先新建一个注解:而这个里面写啥呢?可以看一下Target注解的定义:
所以咱们来定义下属性:
该注解的作用范围为:
所以咱们使用一下:
接下来再多加几个作用域:
- @Retention:描述注解被保留的阶段,@Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到。
而有时候可能需要用这种@Retention(RetentionPolicy.SOURCE),也就是注解只在源码中存在,而在运行期就不可见了,像Android Butterknife框架。 - @Documented:描述注解是否被抽取到api文档中。
- @Inherited:描述注解是否被子类继承。
然后咱们新建一个子类来继续Worker类:
很明显在Teacher类上木有注解,但是由于Worker已经有了MyAnno3注解了,而该注解又声明为了@Inherited,则子类Teacher也会自动继承父类Worker的MyAnno3注解的。
在程序使用(解析)注解:
获取注解中定义的属性值
- 获取注解定义的位置的对象 (Class,Method,Field)。
- 获取指定的注解。
getAnnotation(Class)
//其实就是在内存中生成了一个该注解接口的子类实现对象
public class ProImpl implements Pro{
public String className(){
return "cn.itcast.annotation.Demo1";
}
public String methodName(){
return "show";
}
} - 调用注解中的抽象方法获取配置的属性值。
在上面反射的案例中是采用了配置文件来达到不修改代码就能创建任何类的对象,这次咱们就用注解来代替配置文件【在很多时候其注解都是用来代替配置文件而用的】,达到同样的功能,下面先来新建一个注解:
接下来咱们使用一下该注解,这里再定义两个类,用来进行类方法的声明达到动态生成实例的效果,如下:
那接下来就是解析注解,然后通过反射来达到实例化对象的效果呗,如下:
接着则根据注解对象调用里面的方法来获得相关的值,如下:
为啥能这样调用呢?这里就得理解了:
之后则直接用反射来调用就可以了,跟之前配置的方式一样了,如下:
接着再调用其它类的方法,则只要修改注解既可,如下:
案例:简单的测试框架
比如小明写好了一个自定义的计算器,然后想叫我们来测一下里面的方法,一个个方法来测比较麻烦,此时就可以借助于注解的思路,先新建一个注解:
然后在我们想要测试的方法中加上该注解,如下:
接下来则编写一个测试框架用来进行测试,其实比较简单,这里就直接贴出来了:
/** * 简单的测试框架 * <p> * 当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法),判断方法是否有异常,记录到文件中 */ public class TestCheck { public static void main(String[] args) throws IOException { //1.创建计算器对象 Calculator c = new Calculator(); //2.获取字节码文件对象 Class cls = c.getClass(); //3.获取所有方法 Method[] methods = cls.getMethods(); int number = 0;//出现异常的次数 BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt")); for (Method method : methods) { //4.判断方法上是否有Check注解 if (method.isAnnotationPresent(Check.class)) { //5.有,执行 try { method.invoke(c); } catch (Exception e) { //6.捕获异常 //记录到文件中 number++; bw.write(method.getName() + " 方法出异常了"); bw.newLine(); bw.write("异常的名称:" + e.getCause().getClass().getSimpleName()); bw.newLine(); bw.write("异常的原因:" + e.getCause().getMessage()); bw.newLine(); bw.write("--------------------------"); bw.newLine(); } } } bw.write("本次测试一共出现 " + number + " 次异常"); bw.flush(); bw.close(); } }
编译运行:
然后打开bug.txt瞅一眼:
接下来咱们再来制造一个异常,看能否被检测出来:
运行:
小结: