Java高级技术
单元测试
概述
单元测试就是针对最小的功能单元(方法),编写测试代码对该功能进行正确性测试。
目前的测试方法是怎样的,存在什么问题
- 只能编写main方法,并在main方法中再去调用其他方法进行测试。
- 使用起来很不灵活,无法实现自动化测试。
- 无法得到测试的报告,需要程序员自己去观察测试是否成功。
Junit单元测试框架
JUnit是使用Java语言实现的单元测试框架,它是第三方公司开源出来的,很多开发工具已经集成了Junit框架,比如IDEA。
优点
- 编写的测试代码很灵活,可以指定某个测试方法执行测试,也支持一键完成自动化测试。
- 不需要程序员去分析测试的结果,会自动生成测试报告出来。
- 提供了更强大的测试能力。
- 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
具体使用步骤
- 将Junit框架的jar包导入到项目中(注意:IDEA集成了Junit框架,不需要我们自己手工导入了)。
- 编写测试类、测试类方法(注意:测试方法必须是公共的,无参数,无返回值的非静态方法)。
- 必须在测试方法上使用@Test注解(标注该方法是一个测试方法)。
- 在测试方法中,编写程序调用被测试的方法即可。
- 选中测试方法,右键选择“JUnit运行” ,如果测试通过则是绿色;如果测试失败,则是红色。
- 也可以选择类或者模块启动来测试全部方法。
断言机制
- 可以通过预测测试方法的结果来判断方法是否有bug。
- 使用Assert类的assertEquals()方法。
Junit单元测试框架的常用注解(Junit 4.xxxx版本)
注解 | 说明 |
---|---|
@Test | 测试方法 |
@Before | 用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次 |
@After | 用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次 |
@BeforeClass | 用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次 |
@AfterClass | 用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次 |
- 在测试方法执行前执行的方法,常用于:初始化资源。
- 在测试方法执行后执行的方法,常用于:释放资源。
Junit单元测试框架的常用注解(Junit 5.xxxx版本)
注解 | 说明 |
---|---|
@Test | 测试方法 |
@BeforeEach | 用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次 |
@AfterEach | 用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次 |
@BeforeAll | 用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次 |
@AfterAll | 用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次 |
- 在测试方法执行前执行的方法,常用于:初始化资源。
- 在测试方法执行后执行的方法,常用于:释放资源。
反射(Reflection)
- 反射指的是允许以编程的方式访问已加载类的成分(成员变量、方法、构造器等)。
- 反射的基本作用:在运行时获取类的字节码文件对象,然后可以解析类中的全部成分。
- 反射的核心思想和关键就是:得到编译以后的class文件对象。
反射的内容
- 反射第一步:获取类:Class
- 获取类的构造器:Constructor
- 获取类的成员变量:Field
- 获取类的成员方法:Method
获取类
反射的第一步都是先得到加载后的类,然后才可以去拿类的其他成分。
获取Class类的对象的三种方式
- 方式一:
Class c1 = Class.forName("全类名");
- 方式二:
Class c2 = 类名.class;
- 方式三:
Class c3 = 对象.getClass();
获取构造器
步骤
- 获得class对象。
- 获得Constructor对象。
- 创建对象。
Class类中用于获取构造器Constructor类的方法
方法 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组(只要存在就能拿到) |
Constructor<T> getConstructor(Class<?>... parameterTypes) | 返回单个构造器对象(只能拿public的) |
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造器对象(只要存在就能拿到) |
- 获取构造器的作用依然是初始化一个对象返回。
Constructor类中用于创建对象的方法
方法 | 说明 |
---|---|
T newInstance(Object... initargs) | 根据指定的构造器创建对象 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
- 反射可以破坏封装性,私有的构造器也可以执行了。
获取成员变量
步骤
- 获得class对象。
- 获得Field对象。
- 赋值或者取值。
Class类中用于获取成员变量的方法
方法 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组(只要存在就能拿到) |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象(只要存在就能拿到) |
- 获取成员变量的作用依然是在某个对象中取值、赋值。
Field类中用于取值、赋值的方法
方法 | 说明 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 取值 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
获取成员方法
步骤
- 获得class对象。
- 获得Method对象。
- 运行方法。
Class类中用于获取成员方法的方法
方法 | 说明 |
---|---|
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组(只要存在就能拿到) |
Method getMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象(只要存在就能拿到) |
- 获取成员方法的作用依然是在某个对象中进行执行此方法。
Method类中用于触发执行的方法
方法 | 说明 |
---|---|
Object invoke(Object obj, Object... args) | 运行方法: 参数一:调用该方法的对象 参数二:给该方法传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
反射的作用
- 可以在运行时得到一个类的全部成分然后操作。
- 可以破坏封装性。
- 也可以破坏泛型的约束性(编译时会进行泛型擦除)。
- 更重要的用途是适合:做Java高级框架。
- 基本上主流框架都会基于反射设计一些通用技术功能。
注解
概述
- Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
- Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
自定义注解
自定义注解就是自己做一个注解来使用。
格式
public @interface 注解名称 {
public 属性类型 属性名() default 默认值;
}
- 属性类型:Java支持的数据类型基本上都支持。
- public可以不写,会默认加上。
使用格式
@注解名称(属性名=属性值,...) //没有默认值的属性必须赋值
特殊属性名:value
- 如果只有一个value属性的情况下,使用value属性的时候可以省略value的属性名不写。
- 如果除value外的属性都有默认值且其他属性都不用赋值,那么也可以省略value的属性名不写。
- 但是如果有多个属性, 且存在某个或某些属性没有默认值,那么value的属性名是不能省略的。
注解的原理
- 注解本质是一个接口,Java中所有注解都是继承了Annotation接口的。
@注解(...)
其实就是创建了一个实现类对象,实现了该注解以及Annotation接口。
元注解
元注解就是注解注解的注解。
元注解的类型
元注解有两个: @Target和 @Retention。
@Target(ElementType.类型)
作用:声明被修饰的注解只能在哪些位置使用。
-
TYPE,类,接口
-
FIELD,成员变量
-
METHOD, 成员方法
-
PARAMETER,方法参数
-
CONSTRUCTOR,构造器
-
LOCAL_VARIABLE,局部变量
@Retention(RetentionPolicy.类型)
作用:声明注解的保留周期。
- SOURCE: 只作用在源码阶段,字节码文件中不存在。
- CLASS: 保留到字节码文件阶段,运行阶段不存在。(默认值)
- RUNTIME:一直保留到运行阶段。(开发常用)
注解的解析
注解的解析就是判断类上、方法上和成员变量上是否存在注解,并把注解里的内容给解析出来。
解析注解的指导思想
-
要解析谁上面的注解,就应该先拿到谁。
-
与注解解析相关的接口:
- Annotation:注解的顶级接口,注解都是Annotation类型的对象。
- AnnotatedElement:该接口定义了与注解解析相关的解析方法。
-
所有的类成分Class,Method,Field和Constructor都实现了AnnotatedElement接口,他们都拥有解析注解的能力
方法 | 说明 |
---|---|
Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组。 |
T getDeclaredAnnotation(Class<T> annotationClass) | 根据注解类型获得对应注解对象 |
boolean isAnnotationPresent(Class<Annotation> annotationClass) | 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则返回false |
应用场景
配合反射等技术做框架。
动态代理
概述
代理思想
被代理者没有能力,或者不愿意去完成某件事情,需要找个人(代理)代替自己去完成这件事。
代理
代理就是一个对象,用来对被代理对象的行为进行管理的对象。
动态代理的作用
动态代理主要是对被代理对象的行为进行代理。
动态代理的开发步骤
- 必须定义接口,里面定义一些行为,用来约束被代理对象和代理对象都要完成的事情。
- 定义一个实现类实现接口,这个实现类的对象代表被代理的对象。
- 定义一个测试类,在里面创建被代理对象,然后为其创建一个代理对象返回。(重点)
- 代理对象中,需要模拟收首付款,真正触发被代理对象的行为,然后接收尾款操作。
- 通过返回的代理对象进行方法的调用,观察动态代理的执行流程。
创建代理对象
Java中代理的代表类是:java.lang.reflect.Proxy,它提供了一个静态方法,用于为被代理对象产生一个代理对象返回。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
作用:为被代理对象返回一个代理对象。
参数一:用于指定类加载器,来加载代理类,产生代理对象,使用当前类名.class.getClassLoader()即可。
参数二:真实业务对象的接口。(被代理的方法交给代理对象)
参数三:代理的核心处理程序。
通过代理对象调用方法的执行流程
- 先走向代理。
- 代理中的invoke()方法执行。
- 回到代理中,由代理负责返回结果给调用者。
动态代理的优点
- 可以在不改变方法源码的情况下,实现对方法功能的增强,提高了代码的复用。
- 简化了编程工作,提高了开发效率,同时提高了软件系统的可扩展性。
- 可以为被代理对象的所有方法做代理。
- 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接口本身做代理。