Day1学习进度
1.Juit单元测试
2.反射
3.注解
一、Junit单元测试
测试分类:黑盒测试(不需要写代码)、白盒测试(需要写代码,关注具体流程)
Junit是白盒测试中的一种
1.Junit使用:
- 1.定义一个测试类(测试用例)
规范:
包名:xx.xx.test
类名:xxTest(CalculatorTest)
- 2.定义测试方法(可以独立运行)
规范:
方法名:testxx(testAdd)
参数:空参(不被调用)
返回值:void(不被调用)
- 3.给方法加@Test注解(导入junit的包)
2.判定结果
- 红色:失败
- 绿色:成功
一般会用断言操作来处理结果
Assert.assertEquals(期望的结果,运行的结果);
3.补充注解
-
@Before 修饰的方法在测试之前被执行
-
@After 修饰的方法在测试之后被执行
注:无论测试方法是否有异常,均会执行
4.例子
//Calculator类
public class Calculator {
public int add(int a, int b) {
// int s = 3/0;
return a+b;
}
public int div(int a, int b) {
return a-b;
}
}
//Calculator的测试方法
public class CalculatorTest {
/*
* 初始化方法:
* 用于资源申请,所有测试方法在执行之前都会执行该方法
*/
@Before
public void init() {
System.out.println("申请了资源");
}
/*
* 释放资源方法:
* 所有测试方法在执行完成后都会执行该方法,不论test测试中是否出现异常
*/
@After
public void close() {
System.out.println("释放了资源");
}
@Test
public void testAdd() {
// System.out.println("...");
Calculator c = new Calculator();
int result = c.add(1,1);
//断言
Assert.assertEquals(2,result);
System.out.println("add" + result);
}
@Test
public void testDiv() {
Calculator c = new Calculator();
int result = c.div(1,1);
Assert.assertEquals(0,result);
System.out.println("div" + result);
}
}
二、反射(框架设计的灵魂)
反射:将类的各个组成部分封装为其他对象,称为反射机制
框架:半成品的软件(后续基于框架进行软件开发,简化编码)
类加载器
1.反射机制的好处:
- 1.可以在程序运行过程中,操作这些对象
- 2.可以解耦,提高程序的可扩展性
2.获取Class对象的方式:
-
Class cls1 = Class.forName("com.company.domain.Person"); //将字节码文件加载进内存,并返回Class对象 //多用于配置文件,将类名定义在配置文件中,读取文件,加载类
-
Class cls2 = Person.class; //多用于参数传递
-
Class cls3 = person.getClass(); //多用于对象获取字节码方式
例子:
public class ReflectDemo1 {
/*
* 获取Class的三种方式
*/
public static void main(String[] args) throws Exception {
//第一种方式:Class.forName("全包名")
//ClassNotFoundException:包名写错了
Class cls1 = Class.forName("com.company.domain.Person");
//第二种方式:类名.class
Class cls2 = Person.class;
//第三种方式:对象.getClass()
Person person = new Person();
Class cls3 = person.getClass();
System.out.println(cls1 + " " + cls2 + " " + cls3);
//==比较三个对象的内存地址
System.out.println(cls1 == cls2); //true
System.out.println(cls1 == cls3); //true
//结论:同一个字节码文件(*.class)在一次程序运行过程中只会被加载一次
}
}
3.Class对象功能
获取功能:获取类名、成员变量们、构造方法们、成员方法们
①获取成员变量
-
获取成员变量的方法
- Field[] getFields():获取public修饰的成员变量
- Field getField(“成员变量名”):获取指定public修饰的成员变量
- Field[] gettDeclaredFields():获取所有成员变量
- Field getDeclaredField(“成员变量名”)
-
Field:成员变量的操作
- 设置值 void set(Object obj, Object value)
- 获取值 Object get(Object obj)
- 暴力反射 void setAccessible(boolean flag)
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
Class cls = Person.class;
//一、获取变量名
//1.Field[] getFields():获取public修饰的成员变量
Field[] fields = cls.getFields();
for (Field field:fields) {
System.out.println(field);
}
System.out.println("---------------------------");
//2.Field getField("成员变量名"):获取指定public修饰的成员变量
Field field = cls.getField("a");
System.out.println(field);
Person p1 = new Person();
Object value1 = field.get(p1);//获取了值
System.out.println(value1);
field.set(p1,10);//设置了值
System.out.println(p1);
System.out.println("============================");
//3.Field[] gettDeclaredFields():获取所有成员变量
Field[] fields1 = cls.getDeclaredFields();
for (Field field1:fields1) {
System.out.println(field1);
}
System.out.println("----------------------------");
//4.Field getDeclaredField("成员变量名")
Field field1 = cls.getDeclaredField("d");
System.out.println(field1);
Person p2 = new Person();
//忽略访问权限修饰符的安全检查
field1.setAccessible(true); //暴力反射
Object value2 = field1.get(p2); //IllegalAccessException
System.out.println(value2);
}
}
②获取构造方法
- Constructor:构造方法的操作
- 创建对象 newInstance()
③获取成员方法
- Method:方法对象
- Object invoke(Object obj,Object… args)
④获取类名
personClass.getName();//返回全包名
4.案例
用反射机制实现:创建任意类的对象,并执行其中任意的方法
//pro.properties文件
className=com.company.domain.Student
methodName=study
//ReflectTest.java
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.加载配置文件
Properties pro = new Properties();
//获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
pro.load(resourceAsStream);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载该类进内存
Class aClass = Class.forName(className);
//4.创建对象
Object obj = aClass.newInstance();
//5.获取方法对象
Method method = aClass.getMethod(methodName);
//6.执行该方法
method.invoke(obj);
}
}
//Student.java
public class Student {
public void study() {
System.out.println("卷啊卷啊卷啊卷!");
}
}
只要修改配置文件,就可以创建任意对象,执行其中的任意方法。
三、注解
注解:
- 用来说明程序的
- JDK1.5后的新特性
- 作用分类:编写文档(生成doc文档)、编译检查、代码分析(使用反射)
- 使用注解:@注解名称
1.JDK中预定义的一些注解
- @Override:检测被该注释标注的方法是否是继承自父类(接口)的
- @Deprecated:标识的内容,表示已过时
- @SuppressWarnings:压制警告(一般传递“all”)
2.自定义注解
格式:
元注解
public @interface 注解名称{}
①本质
//自定义注解
public @interface MyAnno {
属性列表;
}
反编译后结果(本质:接口,该接口默认继承Annotation接口)
public interface MyAnno extends java.lang.annotation.Annotation {
}
②属性(接口中的抽象方法)
要求:
- 1.属性的返回值类型:基本数据类型、String、枚举、注解、以上类型的数组
- 2.定义了属性使用时需要赋值(可以使用default默认;只有一个属性需要赋值,且叫value,可以省略)
③元注解(用于描述注解的注解)
- @Target:描述注解能够作用的位置(类、方法、成员变量……)
- @Retention:描述注释被保留的阶段(source、class、runtime)
- @Documented:描述注解是否被抽取到api文档中
- @Inherited:描述注解是否被子类自动继承
3.在程序使用(解析)注解
获取注解中定义的属性值
注解一般用来替换配置文件,简化配置文件的工作
- 1.获取注解定义位置的对象(class,method,field)
- 2.获取注解(getAnnotation(xx.class))
- 3.获取注解定义的属性值
例子:创建任意类的对象,并执行其中任意的方法
//Pro.java:注解类
//描述需要执行的类名和方法名
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
String className();
String methodName();
}
//ReflectTest.java
@Pro(className = "com.company.domain.Student",methodName = "study")
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.解析注解
//获取该类的字节码文件对象
Class<ReflectTest> reflectTestClass = ReflectTest.class;
//获取注解
/**
* 其实就是在内存中生成了一个该注解接口的子类的实现对象
* public class ProImpl implements Pro {
* public String className() {
* return "com.company.domain.Student";
* }
* public String methodName() {
* return "study";
* }
* }
* */
Pro annotation = reflectTestClass.getAnnotation(Pro.class);
//调用注解对象中定义的抽象方法,获取返回值
String className = annotation.className();
String methodName = annotation.methodName();
//2.执行注解中要执行的方法
Class aClass = Class.forName(className);
Method method = aClass.getMethod(methodName);
Object obj = aClass.newInstance();
method.invoke(obj);
}
}
//Student.java 同上
4.例子
//Check.java注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
//Calculator.java
public class Calculator {
@Check
public int add() {
return 1 + 2;
}
@Check
public int div() {
return 1 - 2;
}
@Check
public int mul() {
return 1 * 2;
}
@Check
public int dif() {
return 3 / 0;
}
}
//TestCheck.java
public class TestCheck {
public static void main(String[] args) throws IOException {
//1.创建计算器对象
Calculator c = new Calculator();
//2.获取字节码文件对象
Class aClass = c.getClass();
//3.获取所有方法
Method[] methods = aClass.getMethods();
int num = 0;
BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
//4.判断方法上是否有@Check
for(Method method:methods) {
if(method.isAnnotationPresent(Check.class)) {
try {
method.invoke(c);
} catch (Exception e) {
//5.捕获异常,记录到文件中
num++;
bw.write(method.getName() + "出现异常");
bw.newLine();
bw.write("异常名称:" + e.getCause().getClass().getSimpleName());
bw.newLine();
bw.write("异常原因:" + e.getCause().getMessage());
bw.newLine();
}
}
}
bw.write("共出现了" + num + "次异常");
bw.flush();
bw.close();
}
}
结果