java-反射复习
一、概述
如何在运行时识别一个对象的类型和所属类的信息,一种是假设在编译时已经知道了所有的类型,也就是程序员写的"正向"的代码,一种是反射机制
反射就是把java类中的各种成分(成员变量、方法、构造方法、包)进行解剖,映射成一个个的Java对象
类加载完之后,在堆内存的方法区中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息
这个对象像一面镜子,透过这个镜子可以看到类的结构,形象称之为反射。
反射机制提供的功能:
(1)在运行时判断任意一个对象所属的类
(2)在运行时构造任意一个类的对象
(3)在运行时判断任意一个类所具有的成员变量和方法
(4)在运行时获取泛型信息
(5)在运行时调用任意一个对象的成员变量和方法
(6)在运行时处理注解
(7)生成动态代理
主要API:
java.lang.Class,用来描述类的类
java.lang.reflect.Method
java.lang.reflect.Field
java.lang.reflect.Constructor
二、Class类和其实例
三、类加载和ClassLoader
AppClassLoader加载自定义的类,也就是程序员写的类(System ClassLoader)
ExtClassLoader,负责加载jre/lib/ext目录下的jar (Extension ClassLoader)
BootstrapClassLoader,C++编写,是JVM自带的类加载器,负责java平台核心类库的类加载,无法直接获取(getParent()获取是null)
//自定义类,使用system classLoader ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //system classLoader的父加载器是扩展类加载器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@255316f2 //扩展类加载器的父加载器是根加载器,根加载器无法获取,结果是null ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2);//null //根加载器加载java核心类库,不能加载自定义的类 ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3);//null
四、创建运行时类的Class对象
获取某个类的Class对象的四种方式:
- 根据类名:类名.class
- 根据对象:对象.getClass()
- 根据全限定类名:Class.forName(全限定类名)
- 类加载器加载:ClassLoder.loadClass(全限定类名)
public void classTest() throws Exception { // 获取Class对象的四种方式 Class clazz1 = User.class; Class clazz2 = new User().getClass(); Class clazz3 = Class.forName("com.test.User"); ClassLoader classLoader = Test.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.test.User"); System.out.pirntln(clazz1==clazz2); //true System.out.pirntln(clazz1==clazz3); //true System.out.pirntln(clazz1==clazz4); //true }
注意:以上四种方式获取User的Class实例都是同一份,都相同
五、获取和调用运行时类的完整结构
//getFields();获取当前运行时类及其父类中声明为public权限的属性
//getDeclaredFields();获取当前运行时类自己声明的属性(不包含父类中的)
//getDeclaredField(String fieldName) 获取指定名的属性
//setAccessible(true)--设置属性可访问
//getMethods();获取当前运行时类及其父类中声明为public权限的方法
//getDeclaredMethods();获取当前运行时类自己声明的方法
//getDeclaredMethod(String methodName,Class parameterType...)
//Method.invoke()---返回Object类型,没有返回值则返回null
//getDeclaredConstructor()----constructor.newInstance()---创建一个对象
六、反射的典型应用:动态代理
静态代理:代理类和代理的目标类(被代理的类)事先是知道的
动态代理:客户端仍然通过代理类调用被代理类的方法,但是代理类是在程序运行时根据需要动态创建代理对象
JDK动态代理的关键类:Proxy,InvocationHandler
public interface Human { void eat(String food); void work(); } /** * 被代理类,实现相同的接口 */ public class Teacher implements Human{ @Override public void eat(String food) { System.out.println("====老师在吃"+food+"====="); } @Override public void work() { System.out.println("====老师的工作是教书"); } } package com.yb.designpattern.proxy.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { /** * 被代理类的对象 */ private Object obj; public MyInvocationHandler(Object obj) { this.obj = obj; } /** * 当通过代理类的对象调用方法时,会自动调用如下方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前置处理逻辑"); Object returnValue = method.invoke(obj, args); System.out.println("后置处理逻辑"); return returnValue; } } package com.yb.designpattern.proxy.dynamic; import java.lang.reflect.Proxy; public class ProxyFactory { /** * 输入被代理类的对象,返回其代理类的对象 * * @param obj 被代理的类的对象 * @return 它的代理类的对象 */ public static Object getProxyInstance(Object obj) { MyInvocationHandler invocationHandler = new MyInvocationHandler(obj); Object o = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), invocationHandler); return o; } } /** * 实现动态代理需要解决的问题: * 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类的对象 * 问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类的方法 */ public class ProxyTest { public static void main(String[] args) { Teacher teacher = new Teacher(); Human proxyInstance = (Human) ProxyFactory.getProxyInstance(teacher); proxyInstance.eat("面包"); proxyInstance.work(); } }