一.单元测试
1.1 什么是单元测试
单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法。因此,单元测试就是针对Java方法进行的测试,进而检查方法的正确性。
1.2 目前测试方法是怎么进行的
- 只有一个main方法,如果一个方法的测试失败了,其他测试方法会受到影响。
- 无法得到测试的结果报告,需要程序员自己去观察测试是否成功。
- 无法实现自动化测试。
1.3 Junit
Junit概述
JUnit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用JUnit编写单元测试。此外,几乎所有的IDE工具都集成了JUnit,这样我们就可以直接在IDE中编写并运行JUnit测试。
- JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
- Junit可以生成全部方法的测试报告。
- 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
1.4 单元测试快速入门
步骤
第一步:将Junit的jar包导入到项目中。
第二步:编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
第三步:在测试方法上使用@Test注解,标注该方法是一个测试方法
第四步:在测试方法中完成被测试方法的预期正确性测试。
第五步:选中测试方法,选择“JUnit运行” ,如果测试良好则是绿色;如果测试失败,则是红色
1.5 Junit常用注解
Junit常用注解(Junit 4.xxxx版本):
Junit常用注解(Junit 5.xxxx版本):
1.6 代码实例
二.反射
2.1 反射概述
反射是指对于任何类,在运行的时候,可以获取类的字节码文件对象,然后可以解析类中的全部成分。例如,在运行时,可以得到:
- 类的构造器对象:Construtor
- 类的成员变量对象:Field
- 类的成员方法对象:Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为反射。
2.2 反射的关键
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象,一个类只有一个Class对象,这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
- 正常方式:类 =》通过new实例化 =》取得实例化对象
- 反射方式:实例化对象 =》getClass()方法 =》得到类完整的结构
反射的第一步都是先得到编译后的Class文件对象,然后就可以得到Class的全部成分。
2.3 反射获取类对象
获取Class类的对象,共有三种方式:
- 方式一:使用Class类中的静态方法forName,装入类,并做类的静态初始化,返回Class的对象代码格式如下:Class c1 = Class.forName("全类名");//全类名就是 src 下的全包名
- 方式二:JVM将使用类加载器,将类加载到内存,但前提是类还没有装入内存,不做类的初始化工作,返回Class的对象,代码格式如下: Class c2 = 类名.Class;
- 方式三:使用Object类中的getClass方法获取,会对类进行静态初始化,代码格式如下: Class c3 = 对象.getClass();
2.4 反射获取构造器对象
步骤:
- 首先得到类对象
- 通过类对象获取构造器对象
- 使用构造器对象创建类的对象
Class类中用于获取构造器的方法:
Constructor类中用于创建对象的方法:
作用
获取构造器的作用依然是获取一个对象并返回。
注意事项
如果某个构造器是私有的,那么需要打开权限(暴力反射),然后再创建对象。
范例
public class TestDemo {
//目标:掌握如何获取构造器对象,并利用构造器对象创建对象
//获取所有公共的构造器对象
@Test
public void getConstructors(){
//1.获得类对象
Class class1 = Student.class;
//2.获得所有公共构造器对象
Constructor[] constructors = class1.getConstructors();
//3.遍历构造器数组
for (Constructor constructor : constructors) {
System.out.println(constructor.getName()+"===>"+constructor.getParameterCount());
}
}
//获取所有的构造器对象,无论构造器是公开的,还是私有的
@Test
public void getDeclaredConstructors(){
//1.获取类对象
Class class2 = Student.class;
//2.获取所有构造器对象
Constructor[] allConstructor = class2.getDeclaredConstructors();
//3.遍历构造器对象
for (Constructor constructor : allConstructor) {
System.out.println(constructor.getName()+"===>"+constructor.getParameterCount());
}
}
//获取单个构造器对象
@Test
public void getConstructor() throws Exception{
//1.获取类对象
Class class3 = Student.class;
//2.获取单个构造器
Constructor constructor = class3.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
//3.通过这个构造器创建对象
Student s = (Student) constructor.newInstance("张三",12);
System.out.println(s);
}
//获取单个构造器对象,无视构造器的访问权限
@Test
public void getDeclaredConstructor() throws Exception{
//1.获取类对象
Class class3 = Student.class;
//2.获取单个构造器
Constructor constructor = class3.getDeclaredConstructor();
//3.暴力反射
constructor.setAccessible(true);
//4.通过这个构造器创建对象
Student student = (Student) constructor.newInstance();
System.out.println(student);
}
}
2.5 反射获取成员变量对象
步骤:
- 首先得到类对象
- 从类对象中获取成员变量对象
Class类中用于获取成员变量的方法:
Field中用于取值、赋值的方法
作用
依然是为某个对象中对应的成员变量取值和赋值。
注意事项
如果某个成员变量是私有的,那么需要打开权限(暴力反射),然后再取值、赋值。
代码示例
public class FieldDemo1 {
//目标:掌握成员变量对象的获取,并对某个对象中相应的成员变量进行取值和赋值
//通过反射获取所有成员变量对象
@Test
public void getFields() throws Exception {
//1.获取类对象
Class class1 = Student.class;
//2.获取所有成员变量对象
Field[] fields = class1.getDeclaredFields();
//3.遍历所有成员变量对象
for (Field field : fields) {
System.out.println(field.getName()+"===>"+field.getType());
}
}
//通过反射获取一个成员变量并赋值
@Test
public void getField() throws Exception {
//1.获取类对象
Class class1 = Student.class;
//2.获取成员变量对象
Field name = class1.getDeclaredField("name");
//3.暴力反射
name.setAccessible(true);
//4.对成员变量赋值
Student s = new Student();
name.set(s,"张三");
//5.打印对象
System.out.println(s);
}
//通过反射获取一个成员变量并取值
@Test
public void getField2() throws Exception {
//1.获取类对象
Class class1 = Student.class;
//2.获取成员变量对象
Field name = class1.getDeclaredField("name");
//3.暴力反射
name.setAccessible(true);
//4.对成员变量取值
Student s = new Student("李四",12);
String s1 = (String) name.get(s);
//5.打印成员变量
System.out.println(s1);
}
}
2.6 反射获取成员方法对象
步骤:
- 首先获取类对象
- 通过类对象获取Method对象
- 运行方法
Class类中用于获取成员方法的方法:
Method类中用于触发执行的方法:
作用
获取成员方法的作用依然是在某个对象中执行此方法
注意事项
如果某个成员方法是私有的,那么需要通过暴力反射打开权限,然后再执行此方法。
2.6 反射的作用
2.6.1 绕过编译阶段为集合添加数据
反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时可以为集合存入其他任意类型的元素。
1.原因
泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是不带泛型的集合类型了,泛型相当于被擦除了。
2.代码示例
2.6.2 通用框架的底层原理
1.需求: 给你任意一个对象,在不清楚对象字段的情况可以把对象的字段名称和对应值存储到文件中去。
2.代码示例
//框架代码:
public class MyBatisUtils {
public static void save(Object o){
try (
PrintStream ps = new PrintStream(new FileOutputStream("junit-reflect-annotation-proxy/src/reflect.txt",true))
){
//1.得到类对象
Class aClass = o.getClass();
ps.println("================="+aClass.getSimpleName()+"=================");
//2.得到类中全部的成员变量对象
Field[] fields = aClass.getDeclaredFields();
//3.得到全部成员变量对象的名字和值
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
String value = field.get(o)+" ";
//4.保存到文件中
ps.println(name+"---"+value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//测试代码:
public class ReflectDemo1 {
public static void main(String[] args) {
//目标:利用反射技术,做一个通用框架
//向对象中添加数据,并将对象传到框架中
Student s = new Student("张三",17,'男',12,"3000");
MyBatisUtils.save(s);
Tercher t = new Tercher("张三",17,'男',"王标");
MyBatisUtils.save(t);
}
}