java中静态语句块、实例代码块、构造器方法这3者的调用顺序

1.1、在JVM类加载机制中,有讲到:将类加载到JVM当中后,才进行类的初始化。所谓初始化阶段,是指:根据程序员写的代码去初始化类变量和其他资源,这句话也可以这么说:初始化阶段是执行类构造器<clinit>()方法的过程。<clinit>()方法是编译器自动收集类中的所有类变量和静态语句块(static{})中的语句合并而成的。知道这一点很重要,而<clinit>()方法里面语句的顺序由源程序代码决定。<clinit>()方法和类实例构造器<init>()方法是不同的。这一点,可以通过调试代码来验证,我用的是Mac版本的idea15,在屏幕的最下面一行,可以看到先执行<clinit>()方法,后执行<init>()方法。
 

1.2、调用完<clinit>()方法后,才会执行类的构造函数<init>()方法。涉及到构造方法的调用、实例代码块的执行。同时,实例化几次类,则进行“实例代码块”和“构造器方法”的几次调用,并且,“实例代码块”优先于“构造器方法”的调用。

 

3、说明:

3.1、名字上的区分

<clinit>()方法的名字:类构造器方法

<init>()方法的名字:  实例构造器方法  or  类的构造函数

3.2、说一下<clinit>()方法

3.2.1、<clinit>()方法中的内容由编译器自动收集类中的2类东西组成:类变量和静态语句块中的语句。在<clinit>()方法中各个语句的排列顺序和java代码顺序保持一致。这样的顺序也决定了:静态语句块中只能访问静态语句块之前的静态变量;定义在它后面的变量,是不能被访问的,但是可以为其赋值。

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

3.2.3、由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块     先于    子类的变量赋值操作。

3.2.4、<clinit>()对于类和接口来说,并不是必需的。因为如果一个类中没有静态语句块,也没有对类变量的赋值操作,那么编译器可以不为这个类生成<clinit>()方法。

3.2.5、接口中不能使用静态语句块,但仍然有变量初始化的赋值操作。因此接口和类一样都会生成<clinit>()。只有当父接口中定义的变量使用是,父接口才会初始化。另外,接口的实现类在初始化时,也一样不会执行接口的<clinit>()方法。

3.2.6、虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类中的<clinit>()方法有很耗时的操作,就可能造成多个线程阻塞,在实际应用中,这种阻塞是很隐蔽的。

注:需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法的那条线程退出<clinit>()方法后,其他线程不会再执行<clinit>()方法。同一个类加载器,一个类型只会初始化一次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值