将Class文件内容加载到内存并校验, 转换解析, 初始化. 在运行期间完成(有利于动态扩展, 不利于提前编译以提高运行速度)
加载
- 通过类的全限定名获取该类的二进制字节流
- 将字节流转化为方法区中的运行时数据结构
- 在内存中生成该类的Class对象, 作为方法区中类的访问入口
可自定义类加载器(重新findClass(), loadClass()方法)
数组的加载
JVM在内存中动态构建, 但其元素需要类加载器加载,
初始化
以下几种情况下需要立即初始化:
- new对象
- getstatic, putstatic, invokestatic(类的静态字段, not final(会在编译时放入常量池))
- 调用静态方法
- reflect包对类反射调用时
- 初始化子类时先初始化其父类
- main()方法的类在启动时
- JDK7动态语言支持, methodHandle实例解析为REF_getStatic, REF_putStatic, REF_invokeStatic, REF_newInvokeSpecial句柄, 且句柄对应的类未初始化需初始化
- JDK8中接口增加default方法, 若接口的实现类需要初始化, 则接口也要初始化
类的初始化
类的初始化顺序:
- 父类静态成员,静态代码块
- 子类静态成员,静态代码块
- 父类普通成员普通代码块
- 父类构造方法
- 子类普通成员普通代码块
- 子类构造方法
如果子类只是用来调用父类的静态成员对象, 如Child.NUMBER, 则仅父类会被初始化.
如果仅访问子类的final静态成员变量, 则子类也不会初始化(编译器放到常量池)
创建一个类的数组对象, 并不会导致该类的初始化. 因为JVM会自动生成一个直接继承于Object类的子类, 直接创建一个newarray, 即为实现了原本类的方法和属性的类(但为私有, 用户不能直接访问), 这个类会控制数组越界, 抛出ArrayIndexOutOfBoundsException, 而不是直接非法内存访问.
接口的初始化
仅用到父接口时(如父接口中的常量)才会初始化父接口