在Java中,反射是一个高级的话题
反射与类加载和
类加载
当程序需要使用某个类时,若该类未被加载入内存,
则系统通过加载、连接、初始化三步实现对这个类的初始化
加载 :
就是指将class文件读入内存,并为之创建一个Class对象
任何类被使用时系统都会建立一个Class对象
连接 :
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化 : 也就是 静态成员变量--静态构造的过程
类的初始化时机,
创建类的实例
访问类的静态变量,或为静态变量赋值
调用类的静态方法
初始化某类的子类
直接使用java.exe运行某个主类
使用反射来强制创建某个类或接口对应的java.lang.Class对象
类加载器
负责将class文件加载到内存中,为之生成对应的Class对象
了解类加载机制能够更好的理解程序的运行
组成
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器、负责java核心类的加载
比如System,String等.位于JDK的JRE的lib目录下rt.jar文件(jdk9以前)
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录下中jar包的加载
在JDK的JRE的lib目录下ext目录
System ClassLoader 系统类加载器(也就是自定义的类)
负责在JVM启动时加载来自java命令的class文件
以及classpath环境变量所指定的jar包和类路径
反射
java的反射是指在运行状态中,对任意一个类都能都能知道这个类的所有属性与方法
要解析一个类必须拿到它的Class对象,使用的就是这其中的方法
Class<T>类
Field getField(String name) //成员变量
public Constructor<T> getConstructor(Class<?>... parameterTypes)//构造方法
Method getMethod(String name, Class<?>... parameterTypes) //成员方法
Constructor<T>类 构造器
类 Method 成员方法
类 Field 成员变量
获取Class对象的三种方式:
A: Object类的getClass方法
B: 数据类型的静态属性.class
C: Class类的静态方法public static Class<?> forName(String className)
这里的类名称需要全路径名称(绝对/相对)
尽量使用编译器的提示(在编译器中直接敲 包名.类名)
或者使用编译器的一些协助,右键类文件 Copy Reference(Idea)
使用场景 :
自使用 : 随意,一般第二种比较方便(文件少)
开发 : 一般第三种
由于第三种是字符串,而非具体类名,我们可以把字符串配置到文件。
修改灵活,不会因为对类的改动而影响整个项目
获取无参构造方法并使用
获取所有公有构造
public Constructor<?>[] getConstructors() throws SecurityException
获取所有构造
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
获取公有单个构造
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
获取单个构造
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
单个构造使用的参数列表都是Class类的可变参数,用以适用参数类型变化
注意 : 可以使用 基本类型.class 而且这和 对应包装类.class不一样 如果使用错误报错(JDK9)
Constructor<?>
实例对象
public T newInstance(Object... initargs) throws InstantiationException,
IllegalAccessException,IllegalArgumentException,InvocationTargetException
但是在未设置访问权限之前,private修饰的构造是无法实例的 IllegalAccessException
设置访问权限 :
在其父类 中有
public void setAccessible(boolean flag) throws SecurityException
可以修改访问权限
其实访问权限的设置是破坏封装性的,所以一般是要对class文件进行加密的,一般追加内容或者对数据作位异或
Field
设置某个类型的值
public void set(Object obj, Object value)
throws IllegalArgumentException,IllegalAccessException
这里是为该成员变量的第一个参数(实例对象)设置值 value
setInt......分别是八大基本数据类型
获取某个类型的值
public Object get(Object obj)
throws IllegalArgumentException,IllegalAccessException
其参数是该类型的实例,可以理解为获取实例的成员
getInt...依然是八大基本数据类型
设置访问权限 :
public void setAccessible(boolean flag) throws SecurityException
非公有的需要设置或者获取都需要设置访问权限
Method
获取某个类型的方法
public Method[] getDeclaredMethods() throws SecurityException
获取该类所有声明方法,但不包括其父类
public Method[] getMethods() throws SecurityException
获取该类所有公共方法包括其父类
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
获取该类公共方法,name为其名称,后面是参数列表可变参数(类型),可以获取父类
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
获取该类自有声明的方法,不可获取父类方法(重写方法可以)
使用获取的方法
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
第一个参数obj是实例对象,后面的可变参数是该方法的参数列表
返回值为该方法运行的返回值
如果非公共方法,则需要设置访问权限
通过反射运行配置文件
提供一个包含类与方法信息的文件,要求通过读取文件运行对应类的方法
这样做有个好处,如果需要修改运行的类.只需要修改配置信息即可
比如运行Student.love Teacher.love .....
通过反射越过泛型检查
动态代理
在开发中往往需要对Demo直接相关的方法做权限验证与日志记录
那么,每添加一个类似的类以及添加类似的方法都需要添加这两步
如果不需要这个步骤了,又要撤销这些操作
这时候java提供了代理技术
在java的java.lang.reflect包下提供了一个Proxy(代理)类
和InvocationHandler(调用处理器)接口
可以通过这个类和接口生成动态代理对象。JDK提供的代理只能针对接口做代理
通过cglib可以对类实现代理
Proxy类的构造方法是protected修饰,受保护不能直接实例
要获得其实例需要使用以下方法 :
1 2 3 4
|
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
|
接下来是生成一个InvocationHandler的实现类实现其invoke方法
invoke方法中proxy这个参数代表的是?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class MyInvocation implements InvocationHandler { private Object target; public void setTarget(Object target) { this.target = target; } public MyInvocation() { } public MyInvocation(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("权限校验"); Object result = method.invoke(target, args); System.out.println("记录日志"); return result; } }
|