虚拟机类的加载和连接都是运行期间完成的。
有且只有四种情况必须对类进行初始化:
1 new,获取静态属性、调用静态方法
2 进行反射调用
3 初始化一个类的时,发现父类没有初始化,立即初始化父类(接口对这一点不做要求)
public class Test{
static{
System.out.println("Test");
}
public static class Subtest extends Test{
public static void main(String[] args) {
System.out.println("start");
}
}
}
Test
start
可以看到先初始化父类输出Test
4 JVM启动时,立即加载main所在的类
关于被动引用的三条:
1 通过子类引用父类的静态字段,不会触发子类的初始化
public class Test{
static int a=10;
static{
System.out.println("Test");
}
public static class Subtest extends Test{
static{
System.out.println("Subtest");
}
}
public static class OtherTest{
public static void main(String[] args) {
System.out.println(Subtest.a);
}
}
Test
10
(2)通过数组定义来引用类,不会触发类的初始化
/**
* console 为空
*/
public class Test{
static int a=10;
static{
System.out.println("Test");
}
public static class OtherTest{
public static void main(String[] args) {
Test[] tests=new Test[10];
}
}
}
(3)引用常量只会触发调用类的初始化,对常量所在的类并不会被初始化,因为常量在编译阶段会存入调用类的常量池中
/**
* 只打印10
*/
public class Test{
public static final int a=10;
static{
System.out.println("Test");
}
public static class OtherTest{
public static void main(String[] args) {
System.out.println(a);
}
}
}
类加载的过程
加载/验证/准备/解析/初始化
加载
(1) 通过全限定名获取定义此类的二进制字节流(zip/jar/jsp/反射…)
(2) 字节流代表的静态存贮结构转化为方法区的运行时数据结构
(3) java堆中生成类对象,作为方法区数据的访问入口
验证
双亲委派模型:
站在jvm角度,类加载器只有启动类加载器和其他加载器。
更细致来分,最开始是启动类加载器,接着是扩展类加载器、再接着是应用程序类加载器,最后是自定义类加载器。越是基础的类越靠上。比如Object,在启动类加载器才能保证何时何地记载到的Object都是同一个类。
加载类的过程就是,一个类接到类加载的请求,会委派给自己的父类加载器完成,只有父类反馈自己无法完成,子类才会自己去完成。
JDBC破坏了双亲加载机制,
在OSGI环境下,双亲加载机制不仅是树状结构,变为网状结构,收到类加载请求后
java.*开头的类委托给父类加载器
否则将委派名单列表的类委派给夫类加载器
否则平级委派给当前组件(Bundle)相关的类加载器