什么是类加载机制
一般JVM使用 Java 类的流程为:先将.java文件编译成.class文件,然后类加载器会读取这个 .class 文件,并转换成 java.lang.Class 的实例。
Java类加载器
针对.class文件的不同类型,Java提供了至少四种ClassLoader 来加载。
- BootstrapClassLoader:被称为根加载器,主要用来加载Java核心类,即$JAVA_HOME/jre/lib 里的核心 jar 文件。如 java.lang、java.math、java.io 等 package 内部的这些Java 运行的基础类。同时,该类比较特殊,不继承Java提供的ClassLoader,而是由JVM内部实现。
- ExtClassLoader:被称为拓展加载器,主要用来加载Java核心拓展类。即 $JAVA_HOME/jre/lib/ext 目录下的 jar 文件。
- AppClassLoader:被称为系统加载类,主要用来加载用户在项目中自己编写的类。
- 另外一种是如果要想远程加载一个类,就必须定义实现一个ClassLoader,并实现其中的findClass()。
那么这几种加载类到底是如何工作的呢?
在Java中采用双亲委托的机制来加载,简单来说每次需要加载一个类,先获取一个AppClassLoader的实例,然后向上级层层请求,由最上级优先去加载,如果上级加载不了,在有各子级去自行加载。
验证代码如下:
public class ClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException
{
Class<?> clazz = Class.forName("com.neu.liuxi.Person");
ClassLoader classLoader = clazz.getClassLoader();
System.out.println("ClassLoader is "+classLoader.getClass().getSimpleName());
while(classLoader.getParent()!=null){
classLoader = classLoader.getParent();
System.out.println("Parent ClassLoader is "+classLoader.getClass().getSimpleName());
}
}
}
打印结果:
ClassLoader is AppClassLoader
Parent ClassLoader is ExtClassLoader
由此说明,的确是按照双亲委托机制进行加载。因为BootstrapClassLoader较为特殊,不继承子ClassLoader,所以看不到。但是源码实现中有体现。
源码分析
下面看一下该机制的实现代码,加深理解。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先,检查是否已经被加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
// 让 parent 加载器去加载
c = parent.loadClass(name, false);
} else {
//如无 parent,说明当前是BootstrapClassLoader,调用 本地方法去 JVM 加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// 如果 parent 均没有加载到目标class,调用自身的 findClass() 方法去搜索
// to find the class.
long t1 = System.nanoTime();
//找到class文件并把字节码加载到内存中
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//加载完字节码后,会根据需要进行验证、解析。由子类实现
resolveClass(c);
}
return c;
}
}
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。注意:反射是在运行的时候进行的,不是在编译的时候运行的。
Java反射机制主要提供了以下功能:
1在运行时判断任意一个对象所属的类。
2在运行时构造任意一个类的对象。
3在运行时判断任意一个类所具有的成员变量和方法。
4在运行时调用任意一个对象的方法,生成动态代理。
获取Java对象
//调用某个类的class属性来获取该类对应的class对象
Class<?> person2 = Person.class;
//使用Class类的forName(String className)静态方法。
Class<?> person = Class.forName("com.neu.liuxi.Person");
//调用某个对象的getClass方法。
Person person = new Person(null, 0);
Class<?> class3 = person.getClass();
- Class类提供了四个public方法,用于获取某个类的构造方法。
Constructor getConstructor(Class[] params) 根据构造函数的参数,返回一个具体的具有public属性的构造函数
Constructor getConstructors() 返回所有具有public属性的构造函数数组
Constructor getDeclaredConstructor(Class[] params) 根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
Constructor getDeclaredConstructors() 返回该类中所有的构造函数数组(不分public和非public属性)
- 四种获取成员方法的方法
Method getMethod(String name, Class[] params)
根据方法名和参数,返回一个具体的具有public属性的方法 Method[] getMethods()
返回所有具有public属性的方法数组 Method getDeclaredMethod(String name, Class[]
params) 根据方法名和参数,返回一个具体的方法(不分public和非public属性) Method[]
getDeclaredMethods() 返回该类中的所有的方法数组(不分public和非public属性)
- 四种获取成员属性的方法
Field getField(String name) 根据变量名,返回一个具体的具有public属性的成员变量
Field[] getFields() 返回具有public属性的成员变量的数组
Field getDeclaredField(String name) 根据变量名,返回一个成员变量(不分public和非public属性)
Field[] getDelcaredField() 返回所有成员变量组成的数组(不分public和非public属性)