类加载机制(二)
一:接口加载机制
先看看这个例子
public class Test1 { public static void main(String[] args) { System.out.println(MyChild15.b); } } interface MyInterface5{ public static int a=5; } interface MyChild15 extends MyInterface5{ public static int b=10; }
console:
10
这个呢?
此处将编译后的.class删掉
public class Test1 { public static void main(String[] args) { System.out.println(MyChild15.b); } } interface MyInterface5{ public static int a=5; } interface MyChild15 extends MyInterface5{ public static int b=10; }
Console:
NoClassDeFoundError
总结:当一个接口初始化时,并不要求初始化父接口初始化,只有正真使用到父接口的时候才会初始化
下面再看一个例子
public class Test1 { public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); System.out.println(singleton.counter1); System.out.println(singleton.counter2); } } class Singleton{ public static int counter1; public static int counter2=0; private static Singleton singleton=new Singleton(); private Singleton(){ counter1++; counter2++; } public static Singleton getInstance(){ return singleton; } }
console:
1 1
相信这个大家都能想到,先调用得到实例的方法,然后初始化
再看看下面这个程序
public class Test1 { public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); System.out.println(singleton.counter1); System.out.println(singleton.counter2); } } class Singleton{ public static int counter1; private static Singleton singleton=new Singleton(); private Singleton(){ counter1++; counter2++;//准备阶段的重要意义 } public static int counter2=0; public static Singleton getInstance(){ return singleton; } }
console:
1 0
这是为什么呢?
因为类加载分为3个阶段,在第3个阶段初始化先为所有变量赋上初值counter1,counter2为0,singleton为null,然后再第三阶段赋值,顺序执行counter2先++为1,然后又被设置为0
再来完整整理一遍类加载的步骤
- 加载:将二进制文件读入java虚拟机
- 验证:
准备:为类分配内存,设置默认值
解析:在类型的常量池中寻找类,接口,字段,方法的符号引用,把符号引用变成直接引用
3.初始化:为类赋予正确的初始值
二:类的加载器
类的加载的最终产品是位于内存当中的Class对象
2种类型的类加载器
- JVM自带的类加载器
- 根类加载器(bootstrap)
- 扩展类加载器(extension)
- 系统类加载器(SyStem)
- 用户自定义类加载器
Java.lang.ClassLoader的子类
自己定制类的加载器
注:类加载器并不需要等到某个类被首次主动使用时再加载它,如果存在.class缺失或异常时,只有在主动使用该类时才会报告错误