1 单元测试
Junit常用注解(4.几版本)
|--------------|--------------------------------|
| @Test | 测试方法 |
| @Before | 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 |
| @After | 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 |
| @BeforeClass | 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 |
| @AfterClass | 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |
Junit常用注解(5.几版本)
|-------------|--------------------------------|
| @Test | 测试方法 |
| @BeforeEach | 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 |
| @AfterEach | 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 |
| @BeforeAll | 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 |
| @AfterAll | 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |
2 反射
反射是在运行时获取类的字节码文件对象,然后可以解析类中的全部成分
- 反射的核心思想和关键:得到编译之后的class文件对象
- 在运行时,可以直接得到这个类的构造器对象Constructor,类的成员变量对象Field,类的成员方法对象Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制
反射的关键:
反射的第一步都是先获取Class类对象
java
// HelloWorld.java -> javac -> HelloWorld.class
// 方式一:类名.class
Class c= HelloWorld.class;
// 方式二:Class.forName("全类名")
Class c2 = Class.forName("包名.类名");
// 方式三:对象.getClass();
HelloWorld h = new HelloWorld();
Class c3 = h.getClass();
java
// 获取类的全名,即包名.类名
String name = Class.forName("包名.类名").getName();
// 只获取类名
String name = Class.forName("包名.类名").getSimpleName();
获取类对象后,我们可以类对象中获取类的成分对象
获取构造器
|-----------------------------------------------------------------------|--------------------------|
| 方法 | 说明 |
| 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的) |
| Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
| Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
| Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
Field类中用于取值、赋值的方法
|-------------------------------------|------|
| void set(Object obj, Object value): | 赋值 |
| Object get(Object obj) | 获取值。 |
获取方法对象
作用:在某个对象中进行执行此方法
|---------------------------------------------------------------------|---------------------------|
| 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) | 运行方法 参数一:用obj对象调用该方法 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |
反射的作用
绕过编译阶段为集合添加数据
反射是在运行时的技术,此时集合的泛型将不能产生约束了,此时可以为集合存入其它任意类型的元素
java
ArrayList<Integer> lists = new ArrayList<>();
list.add(100);
// list.add("ABC"); // 报错
list.add(99);
// 但是下面方法可以添加字符串
Class l = lists.getClass();
Method add = l.getDeclaredMethod("add", Object.class);
add.invoke(lists, "ABC");
反射的作用:
- 可以在运行时得到一个类的全部成分
- 可以破坏封装性
- 破坏泛型的约束性
- 做Java高级框架
基本主流框架都会基于反射设计一些通用技术功能
3 注解
对Java中类、方法、成员变量做标记,然后进行特殊处理,到底做何种处理由业务需求来决定
自定义注解
自定义注解就是自己做一个注解来使用
java
public @interface 注解名称 {
public 属性类型 属性名() default 默认值 ;
}
元注解
注解的注解,放在注解上的注解
@Target:约束自定义注解只能在哪些地方使用
@Retention:申明注解的生命周期
@Target中可使用的值定义在ElementType枚举类中,常用值如下
- TYPE,类,接口
- FIELD, 成员变量
- METHOD, 成员方法
- PARAMETER, 方法参数
- CONSTRUCTOR, 构造器
- LOCAL_VARIABLE, 局部变量
@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下
- SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
- CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
- RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
注解解析
注解的操作中通常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容
- Annotation:注解的顶级接口,注解都是Annotation类型的对象
- AnnotatedElement:该接口定义了与注解解析相关的解析方法
|------------------------------------------------------------------|---------------------------------------|
| Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组。 |
| T getDeclaredAnnotation(Class<T> annotationClass) | 根据注解类型获得对应注解对象 |
| boolean isAnnotationPresent(Class<Annotation> annotationClass) | 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false |
4 动态代理
代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理
关键步骤
必须有接口,实现类要实现接口(代理通常是基于接口实现)
创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象
优点
- 非常灵活,支持任意接口类型的实现类对象做代理,也可以直接为本身做代理
- 可以为被代理对象的所有方法做代理
- 可以在不改变方法源码的情况下,实现对方法功能的增强
- 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率
java
// 返回是代理对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
// loader 是定义代理类的类加载器
// interfaces 代理类要实现的接口列表
// h 代理对象的核心处理逻辑
java
public class ProxyUtil {
public static <T> T getProxy(T obj) {
// 返回一个代理对象,给别人使用。
// Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
// 参数一:类加载器,负责加载代理类的
// 参数二:需要被代理的方法在哪些接口中。
// 参数三:代理对象的核心处理逻辑
return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 核心处理逻辑
// proxy 代理对象:一般不需要理会。
// method 代表正在被代理的方法对象。
// args 被代理方法的参数值。
// 1、记录开始时间
long startTime = System.currentTimeMillis();
// 2、触发真正的业务对象的方法执行。
Object result = method.invoke(obj, args);
// 3、记录结束时间,统计耗时
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "方法耗时:" + (endTime - startTime) / 1000.0 + "s!");
return result; // 4、返回结果。
}
});
}
}
java
/**
动态代理需要接口配合。
*/
public interface UserService {
String login(String loginName, String passWord);
void selectUsers();
void deleteUsers();
}
java
// 创建业务对象
UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
// 调用方法
String time = userService.login("aaa", "1111")