Junit单元测试
针对最小的功能单元:方法,编写测试代码对其进行正确性测试。
Junit单元测试框架:可以用来对方法进行测试,它是第三方公司开源出来的(很多开发工具已经集成了Junit框架,比如IDEA)。可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。
不需要程序员去分析测试的结果,会自动生成测试报告。
单元测试的步骤
- 将Junit框架的jar包导入到项目中(IDEA已经集成了Junit框架)。
- 为需要测试的业务类,定义对应的测试类;为每个业务方法,编写对应的测试方法(公共、无参、无返回值)。
- 测试方法上必须声明
@Test
注解,然后在测试方法中,编写代码调用被测试的业务方法进行测试。 - 开始测试:选中测试方法,右键选择“JUnit运行”,如果测试通过则是绿色;如果测试失败,则是红色。测试的正确性取决于测试方法的编写。
反射
概念
通过反射加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)。
- 加载类,获取类的字节码:Class对象。
- 获取类的构造器:Constructor对象。
- 获取类的成员变量:Field对象。
- 获取类的成员方法:Method对象。
获取Class对象
- 方法一:直接创建。
Class c1 = 类名.class
- 方法二:调用Class提供的静态方法。
public static Class forName(String package);
,其中,package
是全类名 - 方法三:调用Object提供的方法。
Class c3 = 对象.getClass();
获取类的成分
获取构造器
获取到构造器后,可以使用构造器初始化对象返回。
方法 | 说明 |
---|---|
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,表示禁止检查访问控制(暴力反射) |
获取成员变量
获取到成员变量后,可以对成员变量进行赋值取值。
方法 | 说明 |
---|---|
public Field[] getFields() |
获取类的全部public修饰的成员变量 |
public Field[] getDeclaredFields() |
获取类的全部成员变量 |
public Field getField(String name) |
获取类的某个public修饰的成员变量 |
public Field getDeclaredField(String name) |
获取类的某个成员变量 |
方法 | 说明 |
---|---|
void set(Object obj, Object value) |
赋值 |
Object get (Object obj) |
取值 |
public void setAccessible(boolean flag) |
设置为true,表示禁止检查访问控制(暴力反射) |
获取成员方法
获取到成员方法后,可以将成员方法执行。
方法 | 说明 |
---|---|
Method[] getMethods() |
获取类的全部public修饰的成员方法 |
Method[] getDeclaredMethods() |
获取类的全部成员方法 |
Method getMethod(String name, Class<?>... parameterTypes) |
获取类的某个public修饰的成员方法 |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) |
获取类的某个成员方法 |
Method提供的方法 | 说明 |
---|---|
public Object invoke(Object obj, Object.. args) |
触发某个对象的该方法执行 |
public void setAccessible(boolean flag) |
设置为true,表示禁止检查访问控制(暴力反射) |
反射的作用
- 基本作用:可以得到一个类的全部成分然后操作。
- 可以破坏封装性。
- 可以绕过泛型的约束。
- 更重要的用途:适合做Java的框架。基本上,主流的框架都会基于反射设计出一些通用的功能。
注解(重要)
概述
就是Java代码里的特殊标记,比如:@Overide、@Test
等.
作用是:让其他程序根据注解信息来决定怎么执行该程序。
注意:注解可以用在类上、构造器上、方法上、成员变量上、參数上、等位置处。
自定义注解
自己定义注解。
public @interface 注解名称 {public 属性类型 属性名() default 默认值;
}public @interface A {String name();int age() default 18;String[] address();
}
特殊属性名:如果在使用时 注解中只有 一个value属性,使用注解时,value名称可以不写!
public @interface B {String value();
}@B("delete") //这样使用是对的
// ===============
public @interface B {String value();int age() default 18;
}
@B(value = "delete", age = 19)
@B("delete") // 原本是 @B(value = "delete", age = 18)// age=18默认值可省略
注解的原理
注解本质上是一个接口,通过反编译可以看到接口继承了Annotation。
注解中定义的属性全部都是抽象方法。
@注解(...)
:其实就是创建了一个实现类对象,实现了该注解以及Annotation接口。
元注解
元注解就是:注解注解的注解。即对注解进行注解。
@Retention(RetentionPolicy.RUNTIME)
@Target ({ElementType .METHOD})
public @interface Test {
}
@Target
作用:声明被修饰的注解只能在哪些位置使用。
@Target(ElementType.TYPE)
- TYPE, 类,接口
- FIELD,成员变量
- METHOD,成员方法
- PARAMETER,方法参数
- CONSTRUCTOR,构造器
- LOCAL_VARIABLE,局部变量
@Retention
作用:声明注解的保留周期。
@Retention(RetentionPolicy.RUNTIME)
- SOURCE,只作用在源码阶段,字节码文件中不存在。
- CLASS(默认值),保留到字节码文件阶段,运行阶段不存在。
- RUNTIME(开发常用),一直保留到运行阶段。
注解的解析
就是判断类上、方法上成员变量上是否存在注解并把注解里的内容给解析出来。
指导思想:要解析谁上面的注解,就应该先拿到谁。
比如要解析类上面的注解,则应该先获取该类的Class对象,再通过Class对象解析其上面的注解。
Class、Method、Field、Constructor都实现了AnnotatedElement接口,它们都拥有解析注解的能力。
AnnotatedElement接口提供了解析注解的方法 | 说明 |
---|---|
public Annotation[] getDeclaredAnnotations() |
获取当前对象上面的注解 |
public T getDeclaredAnnotation(Class<T> annotationClass) |
获取指定的注解对象 |
public boolean isAnnotationPresent(Class<Annotation> annotationClass) |
判断当前对象上是否存在某个注解 |
Class c1 = Demo.class;
if (c1.isAnnotationPresent(MyTest2.class)) {MyTest2 myTest2 = (MyTest2)c1.getDeclaredAnnotation(MyTest2.class);String[] address = myTest2.address();double height = myTest2.height();String value = myTest2.value();
}
动态代理(设计模式)
认识动态代理
程序为什么需要代理?代理长什么样?
对象如果嫌身上干的事太多的话,可以通过代理来转移部分职责。
对象有什么方法想被代理,代理就一定要有对应的方法。
关键是使用接口。
为Java对象创建代理对象
java.lang.reflect.Proxy
类:提供了为对象产生代理对象的方法。
专门有静态方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类。
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法。
参数三:用来指定生成的代理对象要干什么事情。
创建代理最好使用工具类独立一个方法,代理也要实现接口。
// 明星行为接口
public interface StarService{void sing(String name);String dance;
}
// 明星类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Star implements StarService {private String name;@Overridepublic void sing(String name) {System.out.println(this.name + "表演唱歌:" + name);}@Overridepublic String dance() {System.out.println(this.name + "表演跳舞:魅力四射!");return "谢谢!谢谢";}
}
// 创建代理工具类
public class Proxyutil {
// 创建一个明星对象的代理对象返回。
public static <T> T createProxy(T s){
/**
* 参数一:用哪个类加载器去加载生成的代理类。
* 参数二:用于指定代理类需要实现的接口:明星类实现了哪些接口,代理类就实现些接口。
* 參数二:用于指定代理类需要如何去代理(代理要做的事情〉。
*/T proxy = (T) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),s.getClass().getInterfaces(),new InvocationHandler(){@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 这个匿名内部类用来声明代理对象要干的事// 参数一:proxy接收到代理对象本身(暂时用处不大)// 参数二:method代表正在被代理的方法// 参数三:args代表正在被代理的方法的参数String methodName = method.getName();if("sing".equals(methodName)){System.out.println("准备话筒,收钱20万!");}else if("dance".equals(methodName)){System.out.printLn("准备场地,收钱100万!");}// 真正干活(把真正的明星对象叫过来正式干活)// 找真正的明星对象来执行被代理的行为:method方法Object result = method.invoke(s, args); // 反射的思想return result;}});return proxy;
}
public class Test {public static void main(String(] args) {Star star = new Star ("小小");//创建一个专属于她的代理对象StarService proxy = ProxyUtil.createProxy(star);proxy.sing("《红昭愿》"); //找invokehandler的invoke方法System.out.println(proxy.dance()); //找invokehandler的invoke方法}
}
动态代理解决的实际问题
给对象升升级。
Spring就是一个这样的框架,给你的对象宝宝加个代理,返回一个超级对象宝宝。
切面编程 AOP思想。