类加载器
-
Bootstrap ClassLoader: 根类加载器,负责加载Java的核心类,在jdk/jre/lib/rt.jar
-
Extension ClassLoader:扩展类加载器,负责加载JRE的扩展目录,/jre/lib/ext中JARjar包的类
-
System ClassLoader: 系统类加载器,负责在JVM启动时加载来自java命令的-classpath选项、java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。可通过ClassLoader的静态方法getSystemClassLoader()来获取系统泪加载器。
类加载机制:
-
全盘负责:当一个类加载器负责加载某个class时,该class所依赖的和引用的其他class也将由该累加器负责载入,除非显式使用另外一个类加载器载入。
-
父类委托:先让父类加载器试图加载该class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
-
缓存机制:缓存机制将会保证所有加载过的class都会被缓存,当程序中需要使用某个class时,类加载器先从缓存区搜寻该class,只有当缓存区中不存在该class对象时,系统才会读取该类对应的二进制数据。并将其转换成class对象,存入缓存区中。
Java的根类加载器并不是java实现的,而且由于程序通常无须访问根类加载器,因此访问扩展类加载器的父类加载器时返回null。实际上扩展类的父类加载器是根类加载器。
创建并使用自定义的类加载器:
JVM中除根类加载器之外的所有类加载器都是ClassLoader子类的实例,可以通过扩展ClassLoader的子类,并重写该ClassLoader所包含的方法来实现自定义的类加载器
ClassLoader类有如下两个关键方法:
- loadClass(String name, boolean resolve):该方法为ClassLoader的入口点,根据指定名称来加载类,系统就是调用ClassLoader的该方法来获取指定类对应的Class对象。
- findClass(String name):根据指定名称来查找类。
- defineClass()方法管理JVM的实现,他负责将字节码分析成运行时数据结构,并校验有效性等。
- findSystemClass(String name):从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用defineClass()将原始字节转换成Class对象,以将该文件转换成类。
- static getSystemLoader(): 静态方法,返回系统类加载器。
- getParent(): 获取该类加载器的父类加载器。
- resolveClass(Class<?> c):链接指定的类。
- findLoadedClass(String name):如果此Java虚拟机已加载了名为name的类,则直接返回该类对应的class实例,否则返回null,该方法时类加载缓存机制的体现。
loadClass()方法可以避免覆盖默认类加载器的父类委托、缓冲机制两种策略;执行步骤如下:
- 用findLoadedClass(String)来检查是否已经加载类,如果已经加载则直接返回。
- 在父类加载器上调用loadClass()方法。如果父类加载器为null,则使用根类加载器来加载。
- 调用findClass(String)方法查找类。
URLClassLoader:系统类加载器和扩展类加载器的父类。即可以从本地文件系统获取二进制文件加载类,也可以从远程主机获取二进制文件来加载类。
通过反射查看类信息
获得class对象:
- 使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定名(必须添加完整包名)
- 调用某个类的class属性来获取该类对应的class对象。
- 调用某个对象的getClass()方法。返回该对象所属类的Class对象。
Java 8 新增Executable类,代表可执行的类成员派生Constructor、Method两个子类。
- int getParameterCount():获取该构造器或方法的形参个数。
- Parameter[] getParameters():获取该构造器或方法的所有形参。
Parameter也是Java 8新增,每个Parameter对象代表方法或构造器的一个参数。提供如下常用方法来获取参数信息。
- getModifiers():获取修饰该形参的修饰符。
- String getName():获取形参名。
- Type getParameterizedType():获取带泛型的形参类型。
- Class<?> getType(): 获取形参类型。
- boolean isNamePresent(): 该方法返回带类的class文件中是否包含了方法的形参名信息。
- Boolean isVarArgs():该方法用于判断该参数是否为个数可变的形参。
使用反射生成并操作对象
利用指定的构造器来创建Java对象:
- 获取该类的Class对象。
- 利用Class对象的getConstructor()方法来获取指定的构造器。
- 调用Constructor的newInstance()方法来创建Java对象。
调用方法:当获得某个类对应的class对象后,就可以通过该Class对象的getMethods()方法或者getMethod()方法来获取全部方法或指定方法-返回值是Method数组或者Method对象。
每个Method对象对应一个方法,获得Method对象后,可通过该Method来调用它对应的方法。
Object invoke(Object obj, Object…args):该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参。
当通过Method的invoke()方法来调用对应的方法时,Java会要求程序必须有调用该方法的权限。如果程序确实许哟啊调用某个对象的private方法,可以先调用如下方法:
setAccessible(Boolean flag):将Method对象的accessible设置为指定的布尔值。值为true,指示该Method在使用时应该取消Java语言的访问权限检查;值为false,则相反。
访问成员变量的值:通过Class对象的getFields()或getField()方法可以获取该类所包括的全部成员变量或指定成员变量。Field提供了如下两组方法来读取或设置成员变量值。
getXxx(Object obj):获取obj对象的该成员变量的值。Xxx对应8种基本类型,取消Xxx代表引用类型。
setXxx(Object obj, Xxx val):将obj对象的该成员变量设置成val值。
操作数组:java.lang.reflect包下提供了一个Array类,Array对象可以代表所有的数组,程序可以通过使用Array来动态的创建数组,操作数组元素等。
static Object newInstance(Class<?> componentType, int…length):创建一个具有指定的元素类型、指定维度的新数组。
- static xxx getXxx(Object array, int index):返回array数组中第index个元素。
- static void setXxx(Object array, int index, xxx val): 将array数组中第index个元素的值设为val。
使用反射生成JDK动态代理
Proxy提供了用于创建动态代理类和代理对象的静态方法,是所有动态代理类的父类
- static Class<?> getProxyClass(ClassLoader loader, Class<?>…interfaces):创建一个动态代理类所对应的class对象,该代理类将实现interfaces所指定的多个接口。第一个ClassLoader指定生成动态代理类的类加载器。
- static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。