public class aa { |
执行顺序:
①此处采用 java aa来运行程序,涉及到类加载的相关知识,见----------类加载、连接、初始化,属于主动使用,首先加载aa这个类
加载、连接,为静态变量和静态引用分配内存空间,并且赋予默认值,int为零,引用为null等
然后进行初始化
②静态代码块与静态变量,按顺序执行,执行public static int k =0 ;赋初值,
执行public static aa t1 = new aa("t1") ;在执行构造器之前,按照前后顺序执行初始化块或者初始化实例变量(此处不会执行下面的静态代码块和静态变量)
执行public int j = print("j") ;,输出----1:j i=0 n=0
执行 { print("构造块"); } 输出----2:构造块 i=1 n=1
执行构造器new aa("t1");输出----3:t1 i=2 n=2
③ 执行public static aa t2 = new aa("t2") ,步骤同上
输出
4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
④执行public static int i = print("i") ;输出----7:i i=6 n=6
执行public static int n =99 ;
执行静态代码块 static { print("静态块"); } 输出-----8:静态块 i=7 n=99
--------------------------到此为止初始化,结束
⑤接下来执行main方法
aa t = new aa("init") ;又一次主动使用,但是由于不是首次主动使用,类已经加载、连接、初始化结束,不会执行静态代码块等初始化块了。
故仍然按照原则:在执行构造器之前,按照前后顺序执行初始化块或者初始化实例变量
输出-----9:j i=8 n=100
10:构造块 i=9 n=101
11:init i=10 n=102
class A { public class demo extends A { public demo() { |
执行过程:
①此处采用 java demo来运行程序,首次主动使用,加载,连接,初始化demo类,在此之前先对父类
进行加载、连接、初始化。
加载、连接,为静态变量分配内存空间,并且赋予默认值
执行public static int k = 0;----->static { System.out.println("父类静态方法"); // 2 },此时父类初始化结束
输出--------父类静态方法
执行子类加载连接初始化;public static int i=0;------>static { System.out.println("子类静态方法"); }子类初始化结束
输出--------子类静态方法
到此初始化都结束了。
②执行System.out.println("demo print"); //5 输出---------demo print
执行demo d=new demo();
在执行构造器之前,按照先后执行初始化块或初始化实例变量。
继承关系中的执行顺序
父类静态初始化块-子类静态初始化块-父类初始化块-父类构造器-子类初始化块-子类构造器
为什么执行顺序是这样?
这和初始化过程有关,初始化过程是执行类构造器<clinit>()方法的过程,<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,< clinit >()方法与类的构造函数(实例构造器 < init >方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的< clinit >()方法执行之前,父类的< clinit >()方法已经执行完毕,因此在虚拟机中第一个执行的< clinit >()方法的类一定是java.lang.Object。由于父类的< clinit >()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作
依然从父亲开始,int j=0;-------->{ System.out.println("父类非静态方法"); // 9 } 输出------父类非静态方法
执行父类构造器-----》输出 -----父类构造
接下来子类int j=0;//11------->{ System.out.println("子类非静态方法");//12 }输出---------子类非静态方法
执行子类构造器------输出-----子类构造