转 Java类加载的延迟初始化

来源:互联网 发布:淘宝鞋子店铺排行榜 编辑:程序博客网 时间:2024/05/17 07:39

误区:值得注意的是,需要搞清楚 对符号引用的解析 和类的初始化的区别。

 

  链接的最后一步是resolution  , 即对符号引用的解析,但这不是必须的,可以等到相应的符号引用第一次使用时再解析。而类的初始化是在链接之后的(注意了,根据不同JVM有不同的实现方式,在类初始化的时候,可能已经完成了所有的符号引用的解析,也可能没有),本文所写的就是 类的初始化的时机问题。

java类的动态加载机制规定,在类被主动使用(active use)之前,必须已经完成类的初始化。

既然有主动调用,那么就有被动调用了。这两者有哪些区别呢?

主动使用的情况:

1. 创造该类的一个新的实例

2.调用这个类中的静态方法

3.获取类或者接口中的非常量的静态变量

4.利用反射调用方法。

5.初始该类的某子类。

6.被制定为JVM开始运行时必须初始化的类。

注意:

首先,3中为何是“非常量的静态变量”。如果是常量,即声明为final的话,并不会出现对类的构造,虽然调用时有类名出现,但实际调用会直接使用常量,绕过了类的限制(详情见相关constant pool 和 runtime constant pool的知识)。

只有当一个非常量的静态变量被显示的在类或接口中声明时,它的调用才属于主动调用。对于父类中某非常两静态变量的调用属于被动使用(positive use),

 

如下代码


java代码:
查看复制到剪贴板打印
  1. public class Parent {    
  2.      static int i = 10 ;    
  3.     
  4.      static{    
  5.             System.out.println("Parent initiate");    
  6.      }    
  7.          
  8.      public static void func(){    
  9.         System.out.println("func");    
  10.      }    
  11.         
  12.     
  13. }    


java代码:
查看复制到剪贴板打印
  1. public class Son extends Parent{    
  2.     static{    
  3.         System.out.println("Son initiate");    
  4.     }    
  5.         
  6.         
  7. }    


java代码:
查看复制到剪贴板打印
  1. public class Test {    
  2.     
  3.     static{    
  4.         System.out.println("Test initiate");    
  5.     }    
  6.         
  7.     public static void main(String[] args){    
  8.         System.out.println(Son.i);    
  9.         Son.func();    
  10.     }    
  11.         
  12. }    

 

运行的结果是:

java代码:
查看复制到剪贴板打印
  1. Test initiate    
  2. Parent initiate    
  3. 10    
  4. func    



虽然有出现Son,但Son.i访问的是父类的非常量静态变量。于是没有对Son类进行初始化,而只是初始化了明确的声明静态变量的Parent类。

由此可见,一般的,我们在某个类中定义了其他类的成员变量引用,只要该变量没有 new 出一个新的 对象,则JVM也不会初始化这个类,类此时只是被加载了而已,而没有链接 和初始化。

0 0
原创粉丝点击