一:Junit
1. 测试分类
- 黑盒测试
- 不考虑程序内部的逻辑结构和处理过程,只需给定输入观察输出是否符合预期
- 白盒测试
- 关注程序具体的执行流程,并设计测试用例对程序进行调试,检查是否存在bug
2. Junit使用步骤(白盒测试)
- 定义一个测试类(测试用例)
- 测试包名:test
- 测试类名:被测试的类名+Test,如CalculatorTest
- 定义可以独立运行的测试方法(不需要main方法来调用)
- 返回值:void
- 方法名:test+被测试的方法名
- 参数列表:空
- 为此方法添加注解
@Test例如:public void testSum(){}
- 判断测试结果
- 使用
Assert.assertEquals(期待值, 实际值)语句 - 执行测试方法
- 观察控制台的执行结果
- 使用
- 自定义方法
- 初始化方法(示例命名:init)
- 为此方法添加注解
@Before - 用于申请资源,所有测试方法执行前都会自动执行此方法
- 为此方法添加注解
- 释放资源方法(示例命名:close)
- 为此方法添加注解
@After - 用于释放资源,所有测试方法执行完毕后就会自动执行此方法
- 为此方法添加注解
- 初始化方法(示例命名:init)
二:反射
1. 反射机制
- 将类的各个组成部分封装为相应的对象
- 好处
- 可以在程序运行过程中操作这些对象
- 解耦,提高程序的可扩展性
- 好处
2. Java代码在计算机中经历的三个阶段
- 源代码阶段:在硬盘上,未加载进内存
- 类加载器:可以将.class字节码文件加载进内存
3. 获取Class对象的3种方式
Class.forName("全类名"):将字节码文件加载进内存,返回Class对象- 多用于配置文件,将类名定义在配置文件中,读取配置文件,加载进内存
类名.class:通过类名的属性class获取- 多用于传递参数时
对象.getClass():Object类的getClass()方法获取- 多用于已有对象后
- 上述3种方式获取的Class对象相同,说明同一个字节码文件在一次程序运行过程中,只会被加载一次
4. Class对象的功能
- 获取成员变量
- 只能获取public修饰的成员变量
Field[] getFields()Field getField(String name)
- 获取成员变量,无修饰符限制
Filed[] getDeclaredFields()Filed getDeclaredField(String name)
- 只能获取public修饰的成员变量
- 获取构造方法
- 只能获取public修饰的构造方法
Constructor<?>[] getConstructors()Constructor<T>[] getConstructor(Class<?>... parameterTypes)
- 获取构造方法,无修饰符限制
Constructor<?>[] getDeClaredConstructors()Constructor<T>[] getClaredConstructor(Class<?>... parameterTypes)
- 只能获取public修饰的构造方法
- 获取成员方法
- 只能获取public修饰的成员方法
Method[] getMethods()Method getMethod(String name, Class<?>... parameterTypes)
- 获取构造方法,无修饰符限制
Method[] getDeclaredMethods()Method getDeclaredMethod(String name, Class<?>... parameterTypes)
- 只能获取public修饰的成员方法
- 获取全类名
String getName()
5. 所封装的对象的功能
- 获取到privatet修饰的成员变量(构造方法/成员方法)后,可以使用
setAccessible(true)语句(暴力反射)忽略访问权限修饰符的安全检查
- Field:成员变量
- 设置值:
void set(Object obj, Object value) - 获取值:
get get(Object obj)
- 设置值:
- Constructor:构造方法
- 创建对象:
newInstance(Object... initargs)
- 如果使用空参数构造方法创建对象,操作可以简化:
Class对象的newInstance()方法
- 创建对象:
- Method:方法对象
- 执行此方法:
Object invoke(Object obj, Object... args) - 获取方法名称:
String getName()
- 执行此方法:
6. 反射案例
- 需求:写一个简单的框架,在不能改变框架的任何代码的前提下,要求此框架可以帮助我们创建任意类型的对象,并执行其中任意方法
- 步骤分析
- 将需要创建对象的类的全类名(包名.类名)和需要执行的方法先定义在配置文件中
- 在程序中加载并读取配置文件
- 使用反射加载类文件进内存
- 创建对象并执行方法
- 示例代码
import java.io.InputStream; import java.lang.reflect.Method; import java.util.Properties; /* 配置文件:pro.properties 内容: className = demo2.Student methodName = study */ public class ReflectTest { public static void main(String[] args) throws Exception { // 获取类加载器 ClassLoader classLoader = ReflectTest.class.getClassLoader(); // 获取配置文件的字节输入流 InputStream inputStream = classLoader.getResourceAsStream("pro.properties"); // 创建配置文件对象 Properties properties = new Properties(); // 加载配置文件的字节输入流 assert inputStream != null; properties.load(inputStream); // 读取配置文件获取类名 String className = properties.getProperty("className"); // 将类加载进内存 Class<?> class1 = Class.forName(className); // 创建类的对象 Object instance = class1.newInstance(); // 读取配置文件获取方法名 String methodName = properties.getProperty("methodName"); // 获取方法的对象 Method method = class1.getMethod(methodName); // 执行方法 method.invoke(instance); } } - 好处:改动时只需要更改配置文件,而不需要更改代码,提高了程序的可扩展性
三:注解
1. 作用
- 编译检查
- 通过代码里标识的注解让编译器能够实现基本的编译检查
- 编写文档
- 通过代码里标识的注解生成doc文档
- 代码分析
- 通过代码里标识的注解对代码进行分析
2. JDK中预定义的注解
@Override- 表示该注解标识的方法覆盖重写父类(接口)
@Deprected- 表示该注解标识的内容已过时(虽然仍然可以使用,但是已经有了更好的替代方式)
@SuppressWarnings- 压制所有类型的警告:
@SuppressWarnings("all")
- 压制所有类型的警告:
3. 自定义注解
- 格式
元注解 public @interface 注解名称{ 属性列表 } - 本质
- 注解本质上就是一个接口,默认继承Annotation接口
public interface 注解名称 extends java.lang.annotation.Annotation {}
- 属性
- 也就是接口中的抽象方法
- 要求
- 属性的返回值只有以下取值
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
- 如果定义了属性,在使用时需要给属性赋值(一般情况下)
- 如果定义属性时,使用default关键字给属性赋予默认初始值,那么给不给属性赋值都可以
- 如果只有一个属性需要赋值,并且该属性名为value,那么在赋值时可以省略
value=,直接赋值即可 - 为数组赋值时,多个值需要使用{}包裹,如果数组中只有一个值,那么{}可以省略
- 示例代码
public @interface AnnotationTest { public abstract String[] value(); } @AnnotationTest("zs") public class Test { }
- 属性的返回值只有以下取值
- 元注解
- 概念:用于描述注解的注解
- 分类
@Target:描述注解能够作用的位置public @interface Target { ElementType[] value(); }
- 其中枚举类
ElementType的取值有TYPE(类),FIELD(成员变量),METHOD(方法)等取值,例如@Target({ElementType.TYPE, ElementType.METHOD})
@Retention:描述注解被保留的阶段public @interface Retention { RetentionPolicy value(); }
- 其中枚举类
RetentionPolicy的取值有SOURCE(源代码阶段),CLASS(类对象阶段),RUNTIME(运行时阶段),一般自定义注解使用此语句@Retention(RetentionPolicy.RUNTIME),表示当前被描述的注解会保留到class字节码文件中,并被JVM读取到
@Documented:描述注解是否会被抽取到api文档中@Inherited:描述注解是否会被子类继承
- 解析注解
- 在程序中解析(使用)注解,就是获取注解中定义的属性值
- 注解多用于替换配置文件
- 步骤:
- 获取注解作用的位置(CLASS/FIELD/METHOD)的字节码文件对象
- 获取注解对象
- 调用注解对象中定义的抽象方法,获取返回值
- 示例代码
- 利用注解实现反射案例的需求
import java.lang.reflect.Method; @TestAnnotation(className = "demo3.Demo1", methodName = "method1") public class Test { public static void main(String[] args) throws Exception { // 获取该类的字节码文件对象 Class<Test> testClass = Test.class; // 获取注解对象 TestAnnotation annotation = testClass.getAnnotation(TestAnnotation.class); // 调用注解对象中定义的抽象方法,获取返回值 String className = annotation.className(); String methodName = annotation.methodName(); // 加载该类进内存 Class<?> aClass = Class.forName(className); // 创建该类的对象 Object instance = aClass.newInstance(); // 获取方法对象 Method method = aClass.getMethod(methodName); // 执行方法 method.invoke(instance); } }
- 利用注解实现反射案例的需求