首页 > 代码库 > 虚拟机类加载--2.类的初始化

虚拟机类加载--2.类的初始化

初始化是类加载过程的最后一步,但由于比较重要,故放在前面先讲。

在前面的连接(准确来说是准备)阶段,类的变量已经被赋予默认值(如int类型为0,布尔类型为false,引用类型为null等)。而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。或者可以从另一个角度来表达:初始化阶段是执行类构造器<clini>()方法的过程。

1. <clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态语句块(static{})中的语句合并产生的,收集的顺序是由语句在源文件中出现顺序所决定。静态语句块只能访问到定义在静态语句块之前的变量,定义在它之后的变量,可以赋值,但不能访问。如下将报错:

技术分享

 

2. <clinit>()方法与类的构造函数(或者说实例构造器<init>()方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。因此,在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object;

3.由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作,如下将打印2

 1 package com.khlin.initialization; 2  3 public class App { 4  5     public static void main(String[] args) { 6         System.out.println(Sub.b); 7     } 8  9     static class Parent {10         public static int a = 1;11         static {12             a = 2;13         }14     }15 16     static class Sub extends Parent {17         public static int b = a;18     }19 }

 

4.<clinit>()方法对于类或接口并不是必需的,如果类没有静态语句块,也没有赋值动作,那么编译器可以不为这个类生成<clinit>()方法;

5.接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,所以接口和类一样都会生成<clinit>()方法。但接口与类不同的是,执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法。只有当父接口中定义的变量使用时,才会初始化。另化,接口的实现类在初始化时也一样不会执行接口的<clinit>()方法;

6.虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。单例的initialization on demand holder模式就是利用了这一特性。如果在一个类的<clinit>()方法中有耗时很长的操作,就有可能造成多个进程阻塞。需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法的那条线程退出<clinit>()之后,其他线程唤醒后不会再次进入<clinit>()方法。同一个类加载器下,一个类型只会初始化一次。

 

 1 package com.khlin.initialization; 2  3 public class EndlessLoop { 4  5     static { 6         if (true) { 7             System.out.println(Thread.currentThread() + " init EndlessLoop"); 8             while (true) { 9             }10         }11     }12 13 }
 1 public static void main(String[] args) { 2         Runnable runnable = new Runnable() { 3  4             @Override 5             public void run() { 6                 System.out.println(Thread.currentThread() + " is running...."); 7                 EndlessLoop endlessLoop = new EndlessLoop(); 8                 System.out.println(Thread.currentThread() + " runs over"); 9             }10         };11 12         Thread threadA = new Thread(runnable);13         Thread threadB = new Thread(runnable);14         threadA.start();15         threadB.start();16     }

结果会显示:

技术分享

 

虚拟机类加载--2.类的初始化