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

1、分析:

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

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



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

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();
        }
}

输出结果:

blockB
blockA
constructor
blockA
constructor

Process finished with exit code 0


3、说明:

3.1、名字上的区分

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

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

3.2、说一下()方法

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

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

修改后代码:

/**
 * 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);
        }
}


运行结果:
blockB
0
b:1
a:0

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

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

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

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

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

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

你可能感兴趣的:(java,nowcoder笔记)