分享

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

 chfxh 2017-07-25

1、分析:

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





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



2、验证代码:

/** * Created by cxh on 17/07/21. */public class Main { //实例化代码块.每次生成类实例,都会执行.并且,实例化代码块的执行 优先于 构造器. { System.out.println('blockA'); } //静态语句块,在类初始化时,仅仅执行一次. static{ System.out.println('blockB'); } //类实例 public static Main t1 = new Main(); //构造器方法 Main(){ System.out.println('constructor'); } public static void main(String[] args) { //类实例 Main t2 = new Main(); }}

输出结果:

blockBblockAconstructorblockAconstructorProcess finished with exit code 0


3、说明:

3.1、名字上的区分

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

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

3.2、说一下()方法

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


修改后代码:

/** * Created by cxh on 17/07/21. */public class Main { static int a=0; static{ System.out.println('blockB'); System.out.println(a); //System.out.println(b); //报错:Illegal forward reference b=3; } static int b=1; public static void main(String[] args) { //类实例 Main t2 = new Main(); System.out.println('b:'+b); System.out.println('a:'+a); }}


运行结果:
blockB0b:1a:0

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

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

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

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

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

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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多