02_基础加强_其他
反射
概述
反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。
在运行时,可以直接得到这个类的:
-
构造器对象:Constructor
-
成员变量对象:Field
-
成员方法对象:Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。
用法
-
获取类的Class对象
- Class.forName(<全类名>)
- 类名.class
- 对象.getClass
-
获取构造器
方法 说明 Constructor<?>[] getConstructors() 返回所有构造器对象的数组(只能拿public的) Constructor<?>[] getDeclaredConstructors() 返回所有构造器对象的数组,存在就能拿到 Constructor getConstructor(Class<?>... parameterTypes) 返回单个构造器对象(只能拿public的) Constructor getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造器对象,存在就能拿到 构造器类常用方法:
符号 说明 T newInstance(Object... initargs) 根据指定的构造器创建对象 public void setAccessible(boolean flag) 设置为true,表示取消访问检查,进行暴力反射 -
获取成员变量
方法 说明 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对象调用该方法 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写)
作用
-
绕过编译阶段为集合添加数据
反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。
ArrayList<Integer> list = new ArrayList<>(); list.add(100); list.add(“黑马"); // 报错,但通过反射添加则不会
泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了。
-
通用框架底层
如不同类的对象都解析出成员变量名及值并存入日志。
注解
概述
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
范围:类、构造器、方法、成员变量、参数等。
作用:对Java中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定。
例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。
用法
定义:
public @interface 注解名称 {
public 属性类型 属性名() default 默认值 ;
}
解析:注解的解析就是判断是否存在注解,存在注解就解析出内容。
相关接口:
-
Annotation: 注解的顶级接口,注解都是Annotation类型的对象
-
AnnotatedElement:该接口定义了与注解解析相关的解析方法
常用方法:
方法 | 说明 |
---|---|
Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组。 |
T getDeclaredAnnotation(Class |
根据注解类型获得对应注解对象 |
boolean isAnnotationPresent(Class |
判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false |
所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力。
元注解
给注解添加注解的注解~
有2个:
-
@Target: 约束自定义注解只能在哪些地方使用。
可使用的值定义在ElementType枚举类中,常用值如下:
-
TYPE,类,接口
-
FIELD, 成员变量
-
METHOD, 成员方法
-
PARAMETER, 方法参数
-
CONSTRUCTOR, 构造器
-
LOCAL_VARIABLE, 局部变量
-
-
@Retention:申明注解的生命周期。
可使用的值定义在RetentionPolicy枚举类中,常用值如下:
-
SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
-
CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
-
RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
-
动态代理
概述
代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的。
比如每个业务方法都要计算耗时,如果在方法里自己计算,则会出现代码重复,使用代理可以优雅地解决。
实现的关键:
-
必须有接口,实现类要实现接口(代理通常是基于接口实现的)。
-
创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象。
优点:
-
非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。
-
可以为被代理对象的所有方法做代理。
-
可以在不改变方法源码的情况下,实现对方法功能的增强。
-
不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。
用法
创建通用代理工具类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
static <T> T getUserProxy(T object) {
return (T) Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = method.invoke(object, args);
System.out.println(method.getName() + " ran for " + (System.currentTimeMillis() - startTime) / 1000.0 + "s");
return result;
}
});
}
}
创建业务对象并调用:
public class ProxyTest {
public static void main(String[] args) {
User user = ProxyUtil.getUserProxy(new UserImpl());
user.login();
user.update();
user.delete();
}
}
输出: