Java学习-5
Junit单元测试
Junit使用:白盒测试
步骤:
- 定义一个测试类
- 定义一个测试方法:可以独立运行
- 给方法加上@Test注解
- 导入junit依赖
判定结果:一般使用断言操作处理结果
Assert.assertEquals(期望的结果,运算的结果); |
@Before:修饰的方法会在测试方法之前被自动执行
@After:修饰的方法会在测试方法执行之后自动被执行,即使测试方法报错也同样执行
反射
反射:将类的各个组成部分封装为其他对象
反射是框架设计的灵魂,在框架的基础上进行软件开发能够有效的简化编码工作
如图,Java代码在计算机中运行分为如下三个阶段:
其中,由Sourse源代码阶段到Class类对象阶段的过程就是反射机制
反射的好处:
- 可以在程序运行过程中操作这些对象
- 可以降低程序的耦合性,提高程序可扩展性
class对象
获取Class对象的方式
Sourse源代码阶段——**Class.forName(“全类名”)**:将字节码文件加载进内存,返回Class对象——多用于配置文件,将类名定义在配置文件中,读取文件,加载类
其中全类名即包名.类名,如:PersonTest.Person,且由于类名可能有误,该方法可能会出现异常
Class类对象阶段——类名.class:通过类名的属性class获取——多用于参数的传递
RunTime运行时阶段——**对象.getClass()**:getClass()方法定义在Object类中,即被所有类直接或间接继承——多用于对象的获取字节码的方式
同一个字节码文件(xx.class)在一次程序运行中都只会被加载一次,无论通过哪种方式获取的class对象都是同一个
Class对象功能
获取成员变量
Field[] getFields() //获取所有public修饰的成员变量
Field getField(String name) //获取指定名称的 public修饰的成员变量
Field[] getDeclaredFields() //获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)获取构造方法
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
Constructor<?>[] getDeclaredConstructors()获取成员方法
Method[] getMethods() //获取所有public修饰的方法
Method getMethod(String name, 类<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>... parameterTypes)获取类名
String getName()
//使用类对象调用,得到的类名是全类名Field:成员变量
设置值
void set(Object obj,object value)
//传入成员变量名,以及设置给该变量的值获取值
get(Object obj)
忽略访问权限修饰符的安全检查
setAccessible(true); //暴力反射
Constructor:构造方法
创建对象:
T newInstance(Object… initargs) |
如果使用空参创建对象,操作可以简化:使用class对象的newInstance方法
Method:方法对象
执行方法:
Object invoke(Object obj,Object… args) |
获取方法名:
String getName() |
反射-案例
需求:完成一个“框架”,在不改变该类任何代码的前提下,可以创建任意类的对象,并调用其中任意方法
实现:
- 配置文件
- 反射
步骤:
将需要创建的对象的全类名和需要执行的方法定义在配置文件
创建一个pro.properties文件于同目录下,并定义全类名与执行方法,如:
className=test1.test.Person //此处写Person类的全类名
methodName=breathe //此处写需要调用的方法在程序中加载读取配置文件
Properties pro=new Properties(); //创建一个Properties对象
ClassLoader classLoader=Test.class.getClassLoader(); //获取测试类的类加载器
InputStream is=classLoader.getResourceAsStream("pro.properties"); //用类加载器的getResourceAsStream方法获取文件pro.properties路径作为字节流
pro.load(is); //用pro对象调用load方法,加载is路径下配置文件到pro中
String className=pro.getProperty("className");
String methodName=pro.getProperty("methodName");
//获取配置文件中的className和mehodName存为变量使用反射来加载类文件进内存
Class cls=Class.forName(className); //用类名获取类对象
创建对象
Object obj=cls.newInstance(); //用class的newInstance方法创建对象
Method method=cls.getMethod(methodName); //用方法名获取该类中的同名方法执行方法
method.invoke(); //通过方法对象执行方法
通过该种方法,可以在不修改原码的基础上变更创建对象的类和对象调用的方法,只需要修改配置文件中的值即可
注解
也称元数据,放在包、类、字段、方法、局部变量等前面,用于对这些元素进行说明注释,使用注解时写作:@注解名称
作用分类:
编写文档:通过代码里标识的元数据生成文档【通过javadoc命令生成doc文档】
代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【@Override】
JDK中预定义的注解
@Override:用于检测被该注解标注的方法是否继承自父类(接口)
@Deprecated:该注解标注的内容表示已过时
@SuppressWarnings:用于压制警告,一般传递参数all,写作@SuppressWarnings(“all”)
自定义注解
格式:
元注解 |
本质
注解本质上就是一个接口,该接口默认继承自Annotation接口
自定义注解编译为class文件后再用javap命令反编译,java会自动生成一个文件,其文件内容类似如下:
public interface MyAnno extends java.lang.annotation.Annotation{} |
属性
接口中的抽象方法,其要求如下:
- 属性的返回值类型必须如下
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
- 定义了属性,在使用时需要给属性赋值
- 如不想赋值,则需要加默认,即default后加初始值
- 如果只有一个属性需要赋值,且属性为value,则value可以省略,直接写值
- 数组赋值时,值使用{}包裹,若数组只有一个值,则{}可以省略
元注解
用于描述注解的注解,可用JDK已经定义的注解修饰自定义的注解,常用元注解如下:
- @Target:描述注解能够作用的位置
- ElementType取值:
- TYPE:可以作用于类上
- METHOD:可以作用于方法上
- FIELD:可以作用于成员变量上
- ElementType取值:
- @Retention:描述注解被保留的阶段,一般使用RetentionPolicy.RUNTIME,表示自定义的注解会保留到class字节码中,被JVM读取到
- @Documented:描述注解是否被抽取到api文档中
- @Inherited:描述注解是否被其修饰的类的子类继承
程序中使用(解析)注解
主要是用于获取注解中定义的属性值
- 获取注解定义的位置的对象(Class,Method,Field)
- 获取指定的注解:getAnnotation(Class),其实就是在内存中生成了一个该注解接口的子类实现对象
- 调用注解中的抽象方法获取配置的属性值
相关使用
判断一个方法是否被一个注解修饰:
方法名.isAnnotationPresent(注解名.class) |
BufferedWriter对象创建文档并写入:
BufferedWriter bw=new BufferedWriter(new FileWriter("bug.txt")); |
小结:
- 比起自定义注解,已有的注解更加常用
- 注解是给编译器和解析程序使用的
- 注解不是程序的一部分,可以理解为一个标签