类加载机制与反射
Java基础第四篇-类加载机制与反射
1.类的加载、连接和初始化
JVM和类
一个Java程序启动一个Java虚拟机进程,当系统被终结时,JVM进程也会被终止。
有以下几种情况:
程序运行到最后正常结束
程序使用System.exit()结束程序
程序遇到未捕获的异常或者错误而结束
程序平台强制结束了JVM进程
类的加载
首先将类的class文件读入内存,创建一个java.lang.class对象。
类的连接
当类被加载之后,系统会生成一个对应的Class对象,接着把类的二进制数据合并到JRE中
类的初始化
虚拟机对类进行初始化,主要是静态Field进行初始化
2.类加载器
类加载器负责将.class文件加载到内存中,并生成java.lang.class对象。
类加载机制
全盘负责:当一个雷加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由此类加载器负责载入
父类委托:先让parent类加载器试图加载该Class
缓存机制:缓存机制会保证所有加载过的Class都会被缓存,当程序需要使用某个Class时,类加载器先从缓存区找,当缓存区不存在该Class对象,系统才会读取该类对应的二进制数据,转换成Class对象,存入缓存区。这也就是为什么修改了Class,不许重新启动JVM,程序新修改才会生效。
这个例子可以获取根类加载器的核心类库:
public class ClassLoaderPropTest {
public static void main(String[] args) throws IOException {
ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器:" + systemLoader);
Enumeration<URL> eml = systemLoader.getResources("");
while (eml.hasMoreElements())
{
System.out.println(eml.nextElement());
}
ClassLoader extensionLoader = systemLoader.getParent();
System.out.println("拓展类加载器:" + extensionLoader);
System.out.println("拓展类加载器的加载路径" + System.getProperty("java.ext.dirs"));
System.out.println("拓展类加载器的Parent:" + extensionLoader.getParent());
}
}
打印结果:
系统类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
file:/IdeaProject/out/production/IdeaProject/
拓展类加载器:sun.misc.Launcher$ExtClassLoader@60e53b93
拓展类加载器的加载路径/Users/blue/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
拓展类加载器的Parent:null
Extension Classloader被称为扩展类加载器,负责加载JRE的拓展目录中JAR包的类
从上面结果可以看出,系统类加载器的家在路径是当前程序运行路径,拓展类加载器的加载路径是jdk路径,但是拓展类加载器的父加载器是null,说明并不是根类加载器。
最后总结一下类加载器加载Class的步骤
- 检测Class是否再如果,如果有-> step 8
- 如果父类加载器不存在-> step 4,如果父类加载器存在-> step 3
- 请求使用父类加载器去载入Class,成功-> step 8,失败-> step 5
- 请求使用跟雷加载器载入Class,成功-> step 8,失败-> step 5
- 当前类加载器尝试寻找Class文件,找到-> step 6,失败-> step 7
- 从文件中载入Class,成功-> step 8
- 抛出ClassNotFound异常
- 返回java.lang.class对象
3.通过反射查看类信息
获得Class对象
由于每个类被加载后,系统都会生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类,获得Class对象的方式如下:
- 使用Class类的forName(String clazzName)方法,参数是某个类的全限定类名
- 使用某个类的class属性获取对应的Class对象,例如:Person.class
- 使用某个对象的getClass(0方法
从Class中获取信息
大致包含了以下几个方法:
- Constructor getConstructor(Class
4.利用反射生成并操作对象
创建对象
通过反射来生成对象有以下两种方式:
- 使用Class对象的newInstance()方法
- 先使用Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance()方法。
举个例子:
private Object createObject(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> clazz = Class.forName(className);
return clazz.newInstance();
}
调用方法
生成对象后,就可以通过getMethod()方法或者getMethods()方法来获取方法。
再通过invoke()方法来执行
访问属性值
通过getFields()或者getField()方法可以获取Class对象的Field。可以用以下两组方法来读取或者设置Field值
- getXxx(Object obj):获取Field的属性值
- setXxx(Object obj, Xxx val):将obj对象的Field设置成val值