前言
反射机制是java中非常非常重要的东西,比如以前模拟jdk的代理实现和spring的aop实现都用到了反射(见以前设计模式总结),同时,正是由于反射机制,才让我们能更动态的去写程序,比如说在配置文件中配置配置,就能调用某些方法,是在太方便了,下面就开始探索反射机制,下图是类的加载模型
ClassLoad类加载过程
它的特点是1.并非一次性加载
2.需要的时候加载(运行期动态加载)
3.static语句块在加载后执行一次
4.dynamic语句块每次new新的对象都会执行(等同于构造方法中语句)
程序验证
在eclipse中有-verbose:class运行命令可详细的打印出jvm虚拟机第一次在内存中加载类的详细信息,如下:
先看模拟的程序
其中d就是动态方法
以下是输出结果
[Opened E:\myeclipse\jdk1.8\jre\lib\rt.jar]
[Loaded java.lang.Object from E:\myeclipse\jdk1.8\jre\lib\rt.jar]
[Loaded java.io.Serializable from E:\myeclipse\jdk1.8\jre\lib\rt.jar]
。。。。。。
[Loaded com.zwl.tree.A from file:/E:/ecli/shiro/target/classes/]
[Loaded com.zwl.tree.B from file:/E:/ecli/shiro/target/classes/]
[Loaded com.zwl.tree.C from file:/E:/ecli/shiro/target/classes/]
CCCCCCCCCCCCCCCCCCCCCCCCCCC
[Loaded com.zwl.tree.D from file:/E:/ecli/shiro/target/classes/]
DDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDD
[Loaded java.lang.Shutdown from E:\myeclipse\jdk1.8\jre\lib\rt.jar]
[Loaded java.lang.Shutdown$Lock from E:\myeclipse\jdk1.8\jre\lib\rt.jar]
可以发现,jvm在初始化执行程序时需要加载很多类到内存中,并且是动态加载的,就像A和B中间输出语句打印出来,说明不是一次性加载的,这里静态函数只加载了一次,动态方法每次都会加载,其实还有一点,就是jvm会在类加载时,找到jdk中已有的类加载,加载过了一次,就不加载了如D
classLoad类
事实上,jdk中有各种各样的ClassLoad将类加载进内存,大致分为以下几种
一:bootstrap class loader
implemented by native language and load ths core classes of jdk
也就是说,它不是用java写的,而是用操作系统语言,如c、c++写的加载类,它是最核心的类用于加载java中核心的类,当然他没有具体名字
二:extesion class loader
loader the class from jre/lib/ext
三:application class loader
load user-define classes and ClassLoader.getSystemClassLoader()
也就是说它是java的核心类用于加载用户自定义的类和其他类
四:other class loaders
SecureClassLoader、URLClassLoader等等,该类是其他加载类加载其它类
程序验证
测试结果:
null
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader
分析一下:String类bootstrap类加载的类,但是它的加载类是空,而DESKeyFactory的的加载类是扩展类,而第三和第四都是java核心类加载,总之,我们知道classload是有多种并且有顺序和层次的
如
ClassLoader c=Test.class.getClassLoader();
while(c!=null){
System.out.println(c.getClass().getName());
c=c.getParent();
}
结果:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
注意这里的getparent()是得到引用,不是继承。也就是说jdk底层深层次加载是,问上一层有没有加载我这个类,如果加载了就不加载了
动态方法
从classload的角度来看,内存中的一切包括方法等都是对象,也就是说我们可以动态的调用,jdk中为我们提供了类methods类如下所示:
|
|
测试结果:
T loader!
T constructed!
mm
3
void
上面的程序通过class提供的forname()方法将相应的类加载至内存,然后通过newInstance()方法取得实例,methods方法能够得到一个类的所有方法,最后通过public Object invoke(Object obj,Object… args)第二个参数是可变参数,也就是可以有多个参数来调用相应对象的相应方法,而getReturnType()是拿到方法的返回类型,也就是说我们通过class和methods可以动态的调用方法,就好比我们只要在配置文件中配置一下类或方法名,就可以动态的调用类或方法,是在是太方便了
反射一些类常用方法
ClassLoader常用方法
public abstract class ClassLoaderextends Object
构造方法:
protected ClassLoader():使用方法 getSystemClassLoader() 返回的 ClassLoader 创建一个新的类加载器,将该加载器作为父类加载器
protected ClassLoader(ClassLoader parent):使用指定的、用于委托操作的父类加载器创建新的类加载器
public final ClassLoader getParent():返回委托的父类加载器
public URL getResource(String name):查找具有给定名称的资源。
Class常用方法
public final class Class
extends Objectimplements Serializable, GenericDeclaration, Type, AnnotatedElement
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的
public static Class<?> forName(String className)throws ClassNotFoundException:返回与带有给定字符串名的类或接口相关联的 Class 对象
public ClassLoader getClassLoader():返回该类的类加载器
public Method[] getMethods()throws SecurityException:返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
public String getName():以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称
public String getName():查找带有给定名称的资源。
public T newInstance()throws InstantiationException,IllegalAccessException: 创建此 Class 对象所表示的类的一个新实例。
Methods类常用方法
public final class Methodextends AccessibleObjectimplements GenericDeclaration, Member
public String getName():以 String 形式返回此 Method 对象表示的方法名称。
public Class<?> getReturnType():返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。
public Object invoke(Object obj,
Object… args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法,此方法为可变参数
反射的知识其实是很复杂的,这里只做了一次简单的总结,反射的应用也是在太广了,像strcts、spring、hibernate、mybatis、spring mvc包括shiro等等框架,你会发现他们的很多底层实现都基于反射机制。。