反射
笔者对反射的理解就是解剖class文件,来进行一系列操作。
Class类
获取Class类实例的三种方式:
- 类名.class
- 对象.getClass()
static Class forName(String className)
根据类的名称获取类的Class对象(这里的className必须是类的全名)
一般使用第三种方式获取Class对象。
常用成员方法:
Field getField(String name)
获取类中的成员变量的对象String getName()
获取类的全名:包名.类名String getSimpleName()
获取类的简称:类名String getPackageName()
获取包名Field getField(String name)
获取公共成员变量对象Method getMethod(String name, Class<?>... parameterTypes)
获取类中的公共成员函数的对象Constructor<T> getConstructor(Class<?>... parameterTypes)
获取类中的公共构造函数的对象newInstance()
获取该类的实例(JDK 9已过期,使用clazz.getDeclaredConstructor().newInstance() 实例化)
静态:
forName(String className)
加载类
其中成员变量、成员函数、构造函数要素过多,使用了独立的类来描述:
类 | 描述 |
---|---|
Field | 成员变量 |
Method | 成员方法 |
Constructor | 构造方法 |
预定义对象:八中基本类型以及void,其class对象就是其本身
Constructor、Method、Field
Constructor
newInstance(Object... initargs)
获取实例setAccessible(boolean)
是否可以暴力访问,即越过私有权限的检验
setAccessible(boolean)
是其父类的方法,Field、Method、Constructor都可以设置暴力访问私有方法/变量。
Method
Object invoke(Object obj, Object... args)
调用方法,返回值为该方法的返回值(void方法返回null),第一个参数为调用的对象,第二个参数为方法的参数
类加载器
常见的类加载器有三种,每个类加载器负责加载不同位置的类:
加载器 | 说明
| - | - |
Bootstrap 根类加载器 | Bootstrap是最顶级的类加载器。它加载类文件不是我们自己书写的,负责Java核心类的,比如System,String等。只有所有类加载到内存中,我们才可以使用。(Bootstrap自动加载)
ExtClassLoader 扩展类加载器 | ExtClassLoader 扩展类加载器,加载的是扩展类的,我们是用不到的,都是jdk内部自己使用的。(ExtClassLoader类被BootStrap加载)
AppClassLoader 系统/应用类加载器 | AppClassLoader 系统/应用类加载器,是用来加载ClassPath 指定的所有jar或目录,ClassPath表示存放类路径的,我们如果不配置ClassPath,那么就表示当前文件夹,在idea环境下的ClassPath是out目录。在out目录存放的都是我们书写好的class文件,也就是说 AppClassLoader 类加载器是用来加载我们书写的out目录下的class文件。(AppClassLoader类被ExtClassLoader加载)
默认相对目录(文件路径)为相对项目目录,加载器相对目录为src
注解
JDK 常用注解:
注解 | 用途 |
---|---|
@Override |
重写 |
@SuppressWarnings() |
抑制警告 |
@FunctionalInterface |
函数式接口 |
@SuppressWarnings()
参数关键字列表:
参数 | 描述 |
---|---|
all | to suppress all warnings (抑制所有警告) |
boxing | to suppress warnings relative to boxing/unboxing operations(抑制装箱、拆箱操作时候的警告) |
cast | to suppress warnings relative to cast operations (抑制映射相关的警告) |
dep-ann | to suppress warnings relative to deprecated annotation(抑制启用注释的警告) |
deprecation | to suppress warnings relative to deprecation(抑制过期方法警告) |
fallthrough | to suppress warnings relative to missing breaks in switch statements(抑制确在switch中缺失breaks的警告) |
finally | to suppress warnings relative to finally block that don’t return (抑制finally模块没有返回的警告) |
hiding | to suppress warnings relative to locals that hide variable() |
incomplete-switch | to suppress warnings relative to missing entries in a switch statement (enum case)(忽略没有完整的switch语句) |
nls | to suppress warnings relative to non-nls string literals(忽略非nls格式的字符) |
null | to suppress warnings relative to null analysis(忽略对null的操作) |
rawtypes | to suppress warnings relative to un-specific types when using generics on class params(使用generics时忽略没有指定相应的类型) |
restriction | to suppress warnings relative to usage of discouraged or forbidden references |
serial | to suppress warnings relative to missing serialVersionUID field for a serializable class(忽略在serializable类中没有声明serialVersionUID变量) |
static-access | to suppress warnings relative to incorrect static access(抑制不正确的静态访问方式警告) |
synthetic-access | to suppress warnings relative to unoptimized access from inner classes(抑制子类没有按最优方法访问内部类的警告) |
unchecked | to suppress warnings relative to unchecked operations(抑制没有进行类型检查操作的警告) |
unqualified-field-access | to suppress warnings relative to field access unqualified (抑制没有权限访问的域的警告) |
unused | to suppress warnings relative to unused code (抑制没被使用过的代码的警告) |
自定义注解
声明注解
自定义注解使用@interface
声明
其内可以有属性:public abstract 属性类型 属性名() default 属性的默认值;
注解中public abstract
可以省略,并且属性类型只能是是:基本类型
、String
、Class
、注解
,以及以上类型的一维数组,属性可以没有默认值。
使用注解
注解可以没有属性,如果有属性需要在括号内赋值,有默认值可以不赋值。
相同注解在同一方法只能使用一次。
解析自定义注解
元注解
元注解是用来标记注解的注解。
一共有四种:
@Target
用于确定被修饰的注解使用的位置(主要)@Retention
用于确定被修饰的自定义注解生命周期(主要)@Documented
将此注解包含在Javadoc中@Inherited
允许子类继承父类中的注解
@Target
:表示该注解可以用于什么地方。可能的ElementType参数包括:CONSTRUCTOR
:构造器的生命FIELD
:域声明(包括enum实例)LOCAL_VARIABLE
:局部变量声明METHOD
:方法声明PACKAGE
:包声明PARAMETER
:参数声明TYPE
:类、接口(包括注解类型)和enum声明ANNOTATION_TYPE
:注解声明(与TYPE的区别?专门用在注解上的TYPE)TYPE_PARAMETER
:Java8TYPE_USE
:Java8
@Retention
:
表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:SOURCE
:注解将在编译器丢弃CLASS
:注解在class文件中可用,但会被VM丢弃RUNTIME
:VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息
可以通过反射拿到方法,然后拿到注解,通过拿到的注解便可以拿到注解中的值。
代理
静态代理
代理:代理对象主要用来拦截目标业务对象的访问
- 代理对象要持有真实业务对象。(在代理类中创建被代理类的对象。)
- 代理对象要和真实业务对象,具备相同的行为方法。实现同一个接口。
- 代理对象拦截对真实对象的访问,可以修改访问的参数、返回值,甚至拦截访问。
静态代理使用较少。
动态代理
动态代理:就是在程序运行的过程中,动态的生成一个类,这个类要代理目标业务对象,并且可以动态生成这个代理类的对象。
在Java中当某个类需要被代理的时候,要求这个类中的被代理的方法必须抽取到一个接口中,然后这个类需要实现那个接口,只有在这个接口中的方法,代理类才能代理它。
Proxy类
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
:
获得代理类实例,参数:
ClassLoader loader
类加载器,一般给被代理对象的类加载器即可
Class<?>[] interfaces
被代理对象的所有的接口数组
InvocationHandler h
调用处理器,是一个接口。
InvocationHandler
接口有一个静态方法:public Object invoke(Object proxy, Method method, Object[] args)
第一个参数是代理本身,第二个参数是调用的方法,第三个参数是调用方法的参数(在方法中this
是这个接口的实现类,proxy
才是代理本身,通过返回代理本身可以实现链式调用,参考这里)
上个栗子:
@Test
public void proxyTest() throws Exception {
//获取自定义类的Class对象
Class clazz = Class.forName("org.junit.sh.n1.Person");
//获取该类的类加载器
ClassLoader loader = clazz.getClassLoader();
//获取接口数组
Class[] interfaces = clazz.getInterfaces();
// @SuppressWarnings("all")//clazz.newInstance()方法在JDK9中过期了,懒得改了,注解一下
//实例化一个对象作为调用者
Person p = (Person) clazz.newInstance();
//使用Lambda实现InvocationHandler
InvocationHandler h = (proxy, method, args) -> {
if ("show".equals(method.getName())) {
System.out.println("修改方法");
return method.invoke(p, args);
}
return null;
};
//自定义类实现的接口
Showable theProxy = (Showable) Proxy.newProxyInstance(loader, interfaces, h);
//调用方法
theProxy.show();
}
真正理解反射和注解还是得结合框架(。???)ノ
原文地址:https://www.cnblogs.com/lixin-link/p/11116294.html