Class对象在执行引擎中初始化过程
一个class对象在加载到内存需要经过3大步:装载,链接(验证,准备,解析),初始化
装载
什么是装载
java虚拟机查找class文件并生成字节流,然后根据字节流创建Class对象的过程
装载过程
- Classloader通过类的全限定名(包名+类名)来查找.class文件,并生成二进制字节流,class文件可以来自zip,jar,网络的字节流
- 把.class 文件各个部分分别解析为jvm内部特定的数据结构,并存储在方法区
- 创建java.lang.Class类型对象,该对象是外界访问这个类的接口,外界对该类的访问都是通过此接口
加载时机
程序运行的时候,不会把class文件全部加载到内存中,按需装载,有两种情况会对class进行装载
- 隐式装载:在程序运行过程中,当碰到new等方式生成对象时,系统会隐式调用ClassLoader去装载对应的class到内存中
- 显示转载:在编写源代码,主动通过Class.forName()等方法进行class装载操作
链接
链接分为三个步骤:验证,准备,初始化
验证
验证class字节流是否正确,包括class文件格式,代码语法等等
准备
- 静态变量设置”0“值,引用类型默认是null,比如
public static int value = 100;
在准备阶段JVM会设置为0,真正的100,会在初始化的时候设置
- 如果是静态常量
public static final int value = 100;
这种情况准备阶段就是设置为100;
解析
常量池中的符号引用转换成直接引用
初始化
执行类构造器
如,静态常量设置初始值,100
public static int value = 100;
两种加载类方式的区别
Java中Class.forName和classloader都可以用来对类进行加载。
Class.forName(“className”);
其实这种方法调运的是:Class.forName(className, true, ClassLoader.getCallerClassLoader())方法
参数一:className,需要加载的类的名称。
参数二:true,是否对class进行初始化(需要initialize)
参数三:classLoader,对应的类加载器
默认初始化
ClassLoader.laodClass(“className”);
其实这种方法调运的是:ClassLoader.loadClass(name, false)方法
参数一:name,需要加载的类的名称
参数二:false,这个类加载以后是否需要去链接(不需要链接)
默认没有链接
深入理解ClassLoader加载机制
java类何时被加载
调用类构造器或使用的类的静态变量和静态方法
java中的ClassLoader
- 启动类加载器BootstrapClassLoader
- 扩展类加载器PlatformClassLoader
- 系统加载器APPClassLoader
双亲委派
当类加载器收到加载类和资源的请求后,通常委托父类加载器加载,只有当父类加载器找不到指定类和资源之后,自身才会执行实际的类加载操作
自定义ClassLoader
ClassLoader要用到3个方法,findClass,loadClass,defineClass
- Class<?> findClass(String name)
参数数类名,通过全限定名,找到对应的class文件,然后转为字节返回,通过defindClass转换成Class对象 - Class<?> defineClass(byte[] b, int off, int len)
把class字节流转换成Class文件 - Class<?> loadClass(String name)
实现双亲委派,如果需要打破双亲委派,就需要重写此方法
android中的ClassLoader
PathClassLoader,DexClassLoader
- PathClassLoader:用于加载安装系统Apk和安装到手机中Apk内dex文件
- DexClassLoader:用于热修复,可以加载SDK中的jar和apk文件