定义:Java虚拟机把描述类的数据从Class中加载到内存,并且校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是类的加载机制。
与那些在编译是链接的语言不通,java的编译链接过程都是在运行期间完成的,这使得java的灵活性大大提高,java的动态扩展就是依赖于运行期加载和动态链接的机制来完成的。
类的加载时机:类从被加载到内存到卸载出内存需要经历——加载、校验、准备、解析、初始化、使用卸载等过程。校验、准备、解析有称为链接过程。
具体过程可以参考:http://hammer.coding.me/2016/10/26/jvm-1/
加载:把java的Class文件的二进制流加载到方法区内存中成Class对象的过程
校验:校验Class文件是不是虚拟机所识别的文件,是否符合虚拟机规范
准备:将类变量赋初始值。
解析:符号引用替换为直接引用。符号引用只是一个类的描述,而直接引用相当于一个指正,指向了内存地址,所以类必须加载到内存中。
初始化:加载类,初始化类变量调用<clinit>()构造函数。
类加载器
比较两个类是否"相等",只有在这两个类是否是同一个类加载器加载出来的才有意义,如果两个类就算来自同一个Class文件,但是类加载器不同,这两个类也是不相等的。
public static void main(String args[]) {
ClassLoader classLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name)
throws ClassNotFoundException {
String fileName = name.substring(name.lastIndexOf(".")+1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
byte[] b = null;
try {
if(is == null) {
return super.loadClass(name);
}
b = new byte[is.available()];
is.read(b);
} catch (IOException e) {
e.printStackTrace();
}
return defineClass(name, b, 0, b.length);
}
};
Class<?> loadClass = classLoader.loadClass("com.xc.Test");
Object object = loadClass.newInstance();
System.out.println(object.getClass());
System.out.println(object instanceof com.xc.Test);
}
结果:
class com.xc.Test
false
使用流程:
1、重写ClassLoader的loadClass方法,方法参数传入的是类的全限定名
2、根据类名获取Class流
3、把字节流写入一个字节数组
4、根据Class文件的字节数组,调用defineClass方法来装载Class对象到方法区。
5、最后调用Class.newInstance()方法实例化。
其实这就是类的装载过程:类的装载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构
-
双亲委派模型
虚拟机中的类加载器分为两大类:1、启动类加载器,是虚拟机的一部分,有c/c++实现。2、有java实现,程序员可以扩展,其中默认加载器为:扩展类加载器和应用程序加载器。