初始化顺序:
1、静态成员初始化;
先是父类中的静态成员初始化(包括静态域值和静态语句块,按照声明顺序初始化),再是子类成员初始化。这里会一直追溯到最顶层的基类哈。
静态成员的初始化发生在类被加载时,这时不一定调用了构造器。当程序试图访问某一个类时,就会加载此类。
2、非静态成员的初始化;
3、调用构造函数。
注意2和3的顺序为,父类的非静态成员的初始化---->父类构造器----->子类的非静态成员的初始化----->子类构造器。
示例如下:
public class Father { private static int fa_static1 = printInit("static value in father!"); private int fa_instance1 = printInit("instance value in father!"); static { printInit("static block in father!"); } private static int fa_static2 = printInit("another static value in father!"); { printInit("common block in father!"); } private int fa_instance2 = printInit("another instance value in father!"); public Father(){ System.out.println("default father constructor!"); } public Father(int i){ System.out.println("father constructor with parameter i = " + i); } protected static int printInit(String s){ System.out.println(s); return 1; } }
public class Son extends Father{ private static int son_static1 = printInit("static value in son!"); private int son_instance1 = printInit("instance value in son!"); static { printInit("static block in son!"); } private static int son_static2 = printInit("another static value in son!"); { printInit("common block in son!"); } private int son_instance2 = printInit("another instance value in son!"); public Son(){ System.out.println("default son constructor!"); } public Son(int i){ super(i); System.out.println("son constructor with parameter i = " + i); } public static void main(String[] args) { System.out.println("------test1-----------"); Father.printInit("java"); System.out.println("------test2-----------"); Son s1 = new Son(); System.out.println("------test3-----------"); Son s2 = new Son(2); } }
static value in father! static block in father! another static value in father! static value in son! static block in son! another static value in son! ------test1----------- java ------test2----------- instance value in father! common block in father! another instance value in father! default father constructor! instance value in son! common block in son! another instance value in son! default son constructor! ------test3----------- instance value in father! common block in father! another instance value in father! father constructor with parameter i = 2 instance value in son! common block in son! another instance value in son! son constructor with parameter i = 2
解析:
test1:运行Son.java文件,找到函数入口main,这就是试图访问Son的静态函数,需要加载Son类,加载的时候初始化静态量。在加载过程中得到父类Father,所以要先初始化父类的静态量。等两个类的静态量初始化完成后,开始运行main方法,才输出test1.
通过输出结果知:静态成员变量和静态语句块的初始化顺序与声明顺序相同。
test2:无参构造器会自动调用父类的构造器。实例初始化的过程中先初始化成员变量,再调用构造器。
静态绑定与动态绑定:
概念:
绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定。
若程序在执行前进行绑定,由编译器和链接程序实现,叫做前期绑定。C语言中只有一种方法调用,就是前期绑定。
在运行时根据对象的类型进行绑定,叫做后期绑定,也叫动态绑定或运行时绑定。
Java中除了static方法和final方法(private方法被自动认为是final方法)之外,其他所有的方法都是后期绑定。
后期绑定是java中实现的多态的基础。
动态绑定的过程:虚拟机提取对象的实际类型的方法表;虚拟机搜索方法签名;调用方法。
【注意】绑定只是针对方法而言,对于域值没有作用,声明的是什么类,就用什么域值。同时,只有父类和子类同时都有的方法,才能动态绑定。
public class Father { protected String name="父亲属性"; public String getName(){ return name; } } public class Son extends Father{ protected String name="儿子属性"; protected String sonName = "只属于儿子的属性"; public String getSonName() { return sonName; } public String getName(){ return name; } public static void main(String[] args) { Father fa = new Son(); System.out.println("成员调用:" + fa.name); //动态绑定只是针对方法而言,属性还是父亲的属性 System.out.println("方法调用:" + fa.getName()); // System.out.println("成员调用:" + fa.sonName); //不能调用 //不能调用,father中没有此方法。 // System.out.println("方法调用:" + fa.getSonName()); } }