JAVA类加载和初始化

Java程序运行由java虚拟机负责。类从加载到虚拟机内存到卸载出内存,包括

加载-----链接-----初始化-----使用------卸载

链接具体包括:验证-----准备-----解析

 

加载:由类加载器执行,查找字节码并从这些字节码中创建一个Class对象。

链接:验证类中的字节码;为静态域分配存储内存并赋予默认值;解析这个类创建的对其他类的所有引用。

初始化:该类具有基类,则对其初始化,执行静态初始化和静态初始化块。

 

类初始化的时机:程序中首次使用才初始化。

首次主动使用:

1.      创建类的实例

2.      访问类的静态变量

3.      调用类的静态方法

4.      反射调用

5.      初始化类的子类,如果父类为被初始化则先初始化父类

6.      虚拟机启动时指定的启动类(包含main方法)虚拟机会先初始化该类

 

类的初始化步骤:

1,  没有被加载和链接的话就先进行加载连接。

2,  有直接父类并且还没有初始化那就先初始化直接父类。

3,  存在初始化语句,就按顺序依次执行初始化语句。(初始化语句包括静态语句和静态代码块)

 

注:

1.  如果是一个static final的编译器常量(常量池),就不需要对该类进行初始化就可以被读取,但是不是编译器常量对其访问将强制进行类的初始化。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. class A{  
  2.     public static final int b = 10;  
  3.     public static final int c = new Random().nextInt(10);  
  4.     static {  
  5.         System.out.println("A类被初始化");  
  6.     }  
  7. }  
  8. public class Test{  
  9.     public static void main(String[] args) {  
  10.         int i = A.b;  // 不会导致初始化,因为b是一个编译时常量  
  11.         int ii = A.c; // 回导致初始化,c在编译时不能确定  
  12.     }  
  13. }  

2.static域不是final的,那么访问它必须要进行链接和初始化。

3.只有当访问的静态变量或方法是在当前类或接口中定义时,才认为是对类或接口的主动使用。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. interface A{  
  2.     int a = 10;  
  3. }  
  4. class AImp implements A{  
  5.     static {  
  6.         System.out.println("A类被初始化");  
  7.     }  
  8. }  
  9. public class Test{  
  10.     public static void main(String[] args) {  
  11.         int i = A.a;  // 不会导致初始化,访问的变量不是当前类中定义的  
  12.     }  
  13. }  

4.使用.class创建Class的引用时,不会自动的初始化。初始化被延迟到了对静态方法或者静态域的首次调用时才执行。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. class A{  
  2.     public static int b = 10;  
  3.     static {  
  4.         System.out.println("A类被初始化");  
  5.     }  
  6. }  
  7. public class Test{  
  8.     public static void main(String[] args) {  
  9.   
  10.         Class class1 = A.class;  // 没有输出  
  11.         int aa = A.b; //  这句会导致输出  A类被初始化  
  12.             try {  
  13.                 Class class2 = Class.forName("A");  //注释掉上面两句  output: A类被初始化  
  14.             } catch (ClassNotFoundException e) {  
  15.                 e.printStackTrace();  
  16.             }  
  17.     }  
  18. }  

5. 通过数组定义引用类,不会触发此类的初始化。


类中成员的初始化

1.为类中变量赋初值在Java里面可以在定义类成员的地方为其赋值(c++里不能这样),也可以通过构造函数进行初始化,并且构造函无法阻止自动初始化也进行。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. class Test{  
  2. int a;  
  3. Test(){a = 10;}// <span style="font-family: Arial, Helvetica, sans-serif;">a首先被置为0,然后变成7.</span>  
  4. }  

2.变量的初始化顺序取决于变量定义的顺序。他们在任何方法(包括构造函数)调用之前得到初始化。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. class A{  
  2.     public int i = 10;  
  3.     public A(){  
  4.         System.out.println("构造函数->" + i);  
  5.         i = 20;  
  6.         System.out.println("构造函数->" + i);  
  7.     }  
  8.     static {  
  9.         System.out.println("A类被初始化");  
  10.     }  
  11. }  
  12. public class Test{  
  13.     public static void main(String[] args) {  
  14.         A a = new A();  // 执行构造函数之前就饿比赋值成10了,执行构造函数后又被置为20  
  15.         //      output :  
  16.         //      A类被初始化  
  17.         //      构造函数10  
  18.         //      构造函数20  
  19.     }  
  20. }  

3.静态数据的初始化,只占有一份内存区域。静态初始化只有在必要的时刻才会进行。必要的时刻指的是类被主动使用时。

4.初始化顺序是先静态后非静态。前面讨论初始化过程只说静态语句和静态代码块,其实也会有非静态成员属性的初始化,并且是先初始化静态再初始化非静态的。不可能存在非静态成员初始化了而静态成员未被初始化的情况。


一个奇怪的问题

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. class  Singleton{  
  2. //  private static Singleton singleton = new Singleton();  // 1  
  3.     public static int counter1;  
  4.     public static int counter2 = 0;  
  5.     private Singleton(){  
  6.         counter1++;  
  7.         counter2++;  
  8.     }  
  9.     private static Singleton singleton = new Singleton();  // 2  
  10.     public static Singleton getInstance(){  
  11.         return singleton;  
  12.     }  
  13. }  
  14.   
  15. public class Test{  
  16.     public static void main(String[] args) {  
  17.         Singleton singleton = Singleton.getInstance();  
  18.         System.out.println("counter1 = " + singleton.counter1);  
  19.         System.out.println("counter2 = " + singleton.counter2);  
  20.     }  
  21. }  


输出:

放在位置1

1 Singleton singleton =Singleton.getInstance();由于是调用该类的静态方法,因为检查还未加载,接下来会依次加载,链接,初始化。

2 在链接的准备过程中singleton 为null  counter1为0 counter2为0

3 在初始化过程,按顺序初始化

3.1 初始化 private staticSingleton singleton = new Singleton();会执行构造函数,counter1为1,counter2为1

3.2 初始化public static intcounter1; 其实没有初始化的值,因此还是之前的值为1

3.3 初始化public static intcounter2 = 0; 将0赋值给counter2为0

输出为 1 和 0

counter1 = 1

counter2 = 0

 

放在位置2就很容易理解

counter1 = 1

counter2 = 1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值